喝完可乐桶后程序员回归本源,开源Spring基础内容
周六了,又是摸鱼的一天,今天还有点不在状态,脑瓜子迷迷糊糊的,昨晚出去喝可乐桶喝的脑子到现在都不是很正常(奉劝各位可以自己小酌:450ml威士忌+1L多一点可乐刚刚好,可能是我酒量不好),正好没啥事就想整理一下自己的文件夹,发现了很久之前整理的一个spring基础的思维导图,如下:
今天,就以这份思维导图为基础,讲解一下spring基础的内容,好了,我们来看一下文字和代码的详细解析吧
需要这份思维导图的,可以关注公众号:Java架构师联盟,后台回复Java即可
什么是Spring
spring是一个轻量级的控制反转(ioc)和面向切面编程(AOP)的容器框架。
轻量:从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的jar文件里发布;并且Spring所需的处理开销也是微不足道的。
非入侵:在应用中,一般不需要引用springjar包里的类
控制反转:Spring的核心机制,通过控制反转技术促进了松耦合。简单来讲,就是把对象创建的权力交给了容器,让容器来管理对象。
面向切面:允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。AOP是基于代理的,通俗一点就是把 核心业务 和 系统服务(日志系统、权限管理等) 分开。
Spring的核心配置和类
applicationContext.xml:核心配置文件。作用:用于配置所有的类,这些类可以称为springbean
BeanFactory:容器的工厂类(接口)。作用:用于创建或获取springbean,即spring管理的对象。
ApplicationContext:应用上下文(接口)他是BeanFactory的子类 作用:用于创建或获取springbean。功能比BeanFactory强大。BeanFactory和ApplicationContext的区别: BeanFactory:懒加载 需要某个对象再去加载 ApplicationContext:非懒加载 一次性加载所有的对象
Spring IOC
控制反转:把对象的创建、销毁的权利交给容器框架,由容器来管理对象的生命周期。ioc不是新的技术,只是一种思想或理念,实现松耦合。
IOC包括依赖注入(DI,核心) 和 依赖查找。
DI:依赖注入,简单来讲就是在spring实例化对象的时候,由容器来设置这些对象的属性。
spring的注入方式
属性的注入(set方法注入)
前提要有对应的setter方法
以下为spring配置文件代码 java bean忽略。
<bean id="person" class="com.xx.Person">
<property name = "name" value = "xzy"/>
<property name = "coll">
<list>
<value>list</value>
<value>list2</value>
</list>
</property>
<property name = "map">
<map>
<entry key = "age" value = "21" />
</map>
</property>
<property name = "arr">
<array>
<value>java</value>
<value>C++</value>
</array>
</property>
</bean>
通过构造器注入
需要构造方法,javaBean代码:
public class User {
private String name;
private Integer age;
public User(String name,Integer age){
this.name = name;
this.age = age;
}
}
配置文件代码:
<!--通过构造器的方式-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value = "21" />
</bean>
<!--指定下标的方式-->
<bean id="user" class="cn.pojo.User">
<!--指定在构造中的参数名称-->
<bean id = "user" class = "cn.pojo.User">
<constructor-arg value="xxx" name = "name" />
</bean>
注入其他类
<!--通过构造器注入-->
<bean id = "user" class = "com.xx.User">
<!--引用的方式 设置引用id-->
<property name = "car" ref = "car"></property>
</bean>
<bean id = "car" class = "com.xx.Car">
</bean>
<!--内部声明,用这种方式声明,别的bean不能引用了-->
<property name = "car">
</bean>
</property>
bean元素中的属性
id:Bean的唯一标识符
name:通过name对Bean进行管理和配置 name可以多个每个以逗号隔开。
class:指定了Bean的具体实现类,必须是完整的类名 实用类的全限定名
scope:设定Bean实例的作用域,其属性有singleton(单例)、prototype(原型)、request、session、和globalSession,默认值为singleton.
constructor-arg:的子元素,可以传入构造参数实例化 该元素index属性指定构造参数的序号(从0开始).
property:的子元素,通过调用Bean实例中的setter方法完成属性赋值.
ref:property、constructor-arg等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用;
value:property、constructor-arg等元素的子元素,用来直接指定一个常量值;
list:用于封装List或数组类型的依赖注入。
set:用于封装Set或数组类型的依赖注入。
map:用于封装Map或数组类型的依赖注入。
entry:map元素的子元素 用于设定一个键值对。
Bean的实例化
构造器实例化
Spring容器通过Bean对应的默认构造函数来实例化Bean。
静态工厂方式实例化
通过创建静态工厂的方式来创建Bean的实例
public class BeanFactory {
public static Bean createBean(){
return new Bean();
}
}
<!--factory-method-->
<bean id = "bean" class = "com.xx.BeanFactory" factory-method = "createBean"></bean>
实例工厂化方式实例化
不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式.
public class BeanFactory {
public BeanFactory(){
System.err.println("BeanFactory 工厂实例化")
}
public static Bean createBean(){
return new Bean();
}
}
<!--配置工厂-->
<bean id = "beanFactory" class = "com.xx.BeanFactory" /><!--factory-bean 属性指向配置的实例工厂;factory-method属性确定使用工厂的哪个方法-->
<bean id = "bean" factory-bean="beanFactory" factory-method="createBean"/>
Bean的作用域
singleton:单例模式
Spring IOC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。
配置文件:
<bean id ="dog" class = "com.bean.Dog" scope="singleton"></bean>
java代码:
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Dog dog1 = (Dog) ctx.getBean("dog");
System.err.println(dog1);
Dog dog2 = (Dog) ctx.getBean("dog");
System.err.println(dog2);
}
// 输出的结果一致,表明为单例模式。
prototype:原型模式
每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态。
request
在一次Http请求中,容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前HttpRequest内有效。针对每一次Http请求,Spring容器根据该bean的定义创建一个全新的实例,且该实例仅在当前Http请求内有效,而其它请求无法看到当前请求中状态的变化,当当前Http请求结束,该bean实例也将会被销毁。
session
在一次Http Session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效。同Http请求相同,每一次session请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的session请求内有效,请求结束,则实例将被销毁。
Bean的装配方式
基于XML的装配
两种装配方式:setter注入和构造器注入。
设置注入的两个要求:
Bean类必须提供一个默认的午餐构造方法
Bean类必须为需要注入的属性提供对应的setter方法
基于注解(annotation)
常见注解:
@Component 是所有受Spring管理组件的通用形式。
@Repository 持久层使用,dao层使用。
@Service 业务类,表示业务层组件,Service层使用。
@Controller 控制器,表示web层组件
@Autowired 按照类型来装配
@Resource 根据名字去装配
自动装配
属性值说明语法default 默认值由的default-autowire属性值确定default-autowire=“default”byName根据属性名称自动装配byType根据属性的数据类型自动装配constructor根据构造函数参数的数据类型自动装配no不适用自动装配,必须通过ref元素定义
Spring AOP
实现核心业务和系统服务代码之间的分开 通过一种特殊的技术手段来实现核心业务运行时能够实现系统服务的功能;aop的本质是代理 通过对方法进行拦截、增强来实现的。
AOP的基本概念
采用横向抽取机制,把分散在各个方法中的相同的代码抽取出来,然后在编译器或者是运行时再把这些代码应用到所需要执行的地方。
通知(Advice):aop在切点上执行的增强处理。
通知的类型:
前通知(methodBeforeAdvice):方法执行前做增强
后通知(methodAfterAdvice):方法执行后做增强
环绕通知(MethodInterceptor):方法执行前和后做增强
返回通知(AfterReturningAdvice): 成功返回后 进行增强
异常通知(ThrowsAdvice): 抛出异常后 进行通知
切点(Pointcut):就是带有通知的连接点,就是对那些类 哪些方法做增强。
切点的类型:
基于正则表达式 JdkRegexpMethodPointcut
基于AspectJ的表达式 AspectJExpressionPointcut
切面(Aspect):通常上就是一个类,里面定义了 通知 和 切点
AOP = 通知 + 切点
AOP案例 java实现
// com.bean.User
public class User {
public String login(String name){
return "name is "+ name ;
}
public String login(String name , int pwd){
return "name is "+name+", pwd is "+pwd ;
}
}
// 新建一个环绕通知
public class MyAroundAdivice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable{
System.err.println("方法执行前:"+arg0.getMethod().getName()+","+arg0.getArguments()[0]);
Object obj = arg0.proceed();
System.err.println("执行完成后...");
return obj;
}
}
// 新建基于AspectJ切点的测试类
@Test
public void test(){
// 1. 声明通知
Advice advice = new MyAroundAdivice();
//2、基于AspectJ声明切点对象
AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
//3、设置表达式
/*返回类型为String类型 com.bean.User的login方法 参数为String类型*/
//cut.setExpression("execution (String com.bean.User.login(String))");
//任意放回类型 com包下包括com子包下 任意方法 任意的参数0~n
cut.setExpression("execution(* com..*(..))");
//4、切点+通知 =AOP
Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
//5、声明代理bean
ProxyFactory proxy = new ProxyFactory(new User());
//6、设置aop
proxy.addAdvisor(advisor);
//7、从代理中获取代理的对象
User user = (User) proxy.getProxy();
user.login("rose");
System.err.println("---------------");
user.login("jack",123);
}
AspectJ语法
这个目录下的,或是类上的所有…任意的多个。0~N个execution ( com.bean.User.login(String,int))对login方法,必须要接收两个参数,且参数的类型必须是Strig,int的,且返回值无限制。且这个方法必须是User这个类的Execution (* com..(…))返回所有的类型 所有在com包下的类,不包含子包 类的所有方法 接收任意多个参数 0~NExecution (* com….(…))在com包下的,包含子包下的所有类的所有方法,所有参数都切入execution(* com….(String)) || execution(* com….(*,int))|| 逻辑或
AOP案例 基于XML声明式AspectJ
<bean id = "user" class = "com.bean.User">
<!-- 定义一个切面 -->
pointcut-ref="myPointcut" />
pointcut-ref="myPointcut" throwing="e" />
</bean>
基于注解的声明式AspectJ (常用)
注解功能@Aspect注解在类上,声明这是一个切面@Pointcut注解在方法上声明是一个切点,且要声明aspectj的切点表达式@Before前通知@After @AfterRetuning –正确执行成功返回值 @AfterThrow – 错误的执行,抛出异常后通知@Around环绕通知
第一步:新建注解的切面类
@Aspect
@Component
public class MyAspect {
//定义一个切入点表达式 使用一个返回值为void,方法体为空的方法来命名切点
@Pointcut("execution(* com..*(..))")
public void myPointCut(){}
//前置通知
@Before("myPointCut()")
public void doBefore(JoinPoint joinPoint)
{
System.out.println("前置通知,方法开始..."+":目标类是"+joinPoint.getTarget()+",被植入的增强方法是:"+joinPoint.getSignature().getName());
}
//后置通知
@After("myPointCut()")
public void doAfter()
{
System.out.println("后置通知,方法结束...");
}
//返回通知
@AfterReturning("myPointCut()")
public void doReturnAfter()
{
System.out.println("返回通知,方法最终结束...");
}
/**
* 环绕通知 ProceedingJoinPoint用来调用被增强的方法
* 必须是Object的返回类型
* 必须接受一个参数,类型为ProceedingJoinPoint
* 必须throws Throwable
*/
@Around("myPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
{
System.out.println("环绕通知:begin...");
//执行被增强的方法
Object obj = joinPoint.proceed();
System.out.println("环绕通知:end.....");
return obj;
}
@AfterThrowing(value="myPointCut()",throwing="e")
public void doThrowing(Throwable e){
System.out.println("异常通知......."+e.getMessage());
}
}
第二步:xml配置文件
<bean id="user" class="com.bean.User"></bean>
<context:component-scan base-package="com.xxx"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
第三步:测试方法
@Test
public void test1() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/xxx/applicationContext.xml");
User user = ctx.getBean("user",User.class);
user.say();
//user.run();
}
如有问题 请指出