这是我参与更文挑战的第4天,活动详情查看: 更文挑战
九、Spring IOC
1. Spring IOC底层原理
ioc:控制反转。首先需要了解:依赖倒转原则
依赖倒转原则:高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象
控制反转正是依赖倒转原则的一种代码设计的思路。具体采用方法为:依赖注入
具体代码实现参考:
2. BeanFactory
3. 依赖注入(DI)
依赖注入实现方式:
- setter注入
- 构造构造器注入
- 静态工厂方法实例化
- 实例工厂实例化
4. Spring自动装配
Spring
5种装配模式
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,应该使用显式的bean引用来连线。不用为了连线做特殊的事。 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
4. 基于注解(Annotation)的装配方式
JDK1.5之后,提供了Annotation功能,Spring也提供了对Annotation的全面支持,Spring3中定义了一系列注解
-
@Component:可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以用在任何层次
-
@Service 一般用在Service层
-
@Controller 一般用在控制层
-
@Repository 一般用在数据库访问层
-
@Required注解应用于bean属性的setter方法
-
@Autowired注解可以应用到bean属性的setter方法,非setter方法,构造函数和属性
-
@Resource:作用与Autowired一样,区别是@Autowired默认按照Bean的类型装配,@而Resource默认按照Bean的实例类型进行装配,@Resource有name、type属性,Spring将name属性解析为Bean实例名称,将type属性解析为Bean的梳理类型,如果指定name属性,则按照实例名称进行装配,如果指定type属性,按照Bean类型进行装配,如果都不指定,则先按照Bean实例名称装配,如果不能装配,则按照Bean的类型进行装配,如果都不能匹配,抛出NoSuchBeanDefinitionException异常
-
@Qualifier,通过指定确切的将被引用的bean,@Autowired和@Qualifier注解可以用来删除混乱
@Resource和@Autowired的区别
- @Resource和@Autowired都是做bean的注入时使用
- @Resource不是Spring中的注解,但是Spring支持该注解,而@Autowired是Spring的注解
- @Autowired是按照类型(byType)来装配Bean的,不回去匹配name,默认情况下他要求依赖对象必须存在,如果需允许null,可以设置它的required属性为false,如果想让@Autowired按照名称(byName)来装配,则需要配合@Qualifier一起使用
十、Spring AOP
1. 代理模式
面向切面编程,是运行是织入的,那么运行是织入到底是怎么实现的呢?答案就是代理对象。代理对象又分为静态代理和动态代理。
- 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
- 动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理的每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
动态代理与静态代理对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理类。java.lang.reflect包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
2. 静态代理
3. 动态代理
Spring 常见的两种动态代理为:JDK动态代理和CGLIB动态代理
- JDK动态代理:如果目标对象实现了接口,采用JDK动态代理(基于接口)
- CGLIB动态代理:如果目标对象没有实现接口,必须采用CGLIB动态代理(基于继承)
JDK代理
JDK动态代理主要涉及java.lang.reflect
包下的两个类:Proxy类和InvocationHandler接口。
实现的三大要点:
- 通过
java.lang.reflect.Proxy
类来动态生成代理类 - 代理类要实现**
InvocationHandler
接口** - JDK代理只能基于接口进行动态代理
总结:
- 因为利用JdkProxySubject生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有;
- 生成的代理类的所有方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体;
- 利用JDK代理方式必须有接口的存在。
CGLIB代理
采用非常底层的字节码技术,可以为一个类创建一个子类,并在子类中采用方法区技术拦截所有的父类方法的调用,并顺势织入横切逻辑。
CGLib和JDK的原理类似,也是通过方法去反射调用目标对象的方法。
4. Spring两种代理模式
如果目标对象实现了接口,Spring默认使用JDK动态代理
如果没有实现任何接口,Spring则使用CGLIB进行动态代理
需要注意的是:如果目标对象实现了接口,但是强制CGLIB代理是可以的。但是如果没有实现任何接口是不可以强制JDK动态代理的。
5. AOP实现之AspectJ框架
AspectJ中的通知类型
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
使用AspectJ实现AOP有两种方式:一种是基于XML的声明式AspectJ,另一种是基于注解的声明式AspectJ