参考自 : [Spring框架]Spring IOC的原理及详解。
为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用于实现对象之间的“解耦”。
IoC(Inversion of Control):控制反转(是把传统上由程序代码直接操控的对象的生成交给了容器来实现, 通过容器来实现对象组件的装配和管理。所谓“控制反转”就是获得依赖对象的过程被反转了,获得依赖对象的过程由自身管理变为了由IOC容器主动注入, 由容器来创建并管理对象之间的关系。) 在没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。在引入IOC容器之后,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。依赖注入DI(Dependency Inversion)(IOC的别名):所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。【这是IOC的是实现方式】所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。【控制反转(Inversion of Control) 就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入(Dependency Injection)。】IOC中最基本的技术就是“反射(Reflection)”编程,反射,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。
我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的 对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
spring启动时去读取Bean配置信息,并在spring容器中生成一份相应的Bean定义注册表根据这张注册表去实例化Bean装配Bean之间的关系,为上层提供准备就绪的运行环境
ApplicationContext(应用上下文):ApplicationContext接口扩展了BeanFactory接口,它在BeanFactory基础上,又继承了多个接口来提供了一些额外的功能。继承的接口如下: BeanFactory:能够管理、装配BeanResourcePatternResolver:能够加载资源文件MessageSource:能够实现国际化等功能ApplicationEventPublisher:能够注册监听器实现监听机制
Spring中默认返回的都是单例,所以用getBean返回的都是同一个对象
傻傻分不清:Spring IoC注入,自动装配与循环依赖
setter注入: 被注入对象的setter()方法的参数列表声明依赖对象。 setter注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。构造函数注入:被注入对象的构造方法的参数列表声明依赖对象的参数列表【在构造器上加@Autowired或者在配置文件里配置都可以】 它保证一些必要的属性在Bean实例化时就得到设置(construct是bean生命周期的第一步,实例化bean),并且确保了Bean实例在实例化后就可以使用。
AOP主要名词
Aspect:通用功能的代码实现Target:被织入Aspect的对象Join Point:可以作为切入点的机会,所有方法都可以作为切入点Pointcut:Aspect实际被应用在的Join Point,支持正则Advice:类里的方法以及这个方法如何织入到目标方法的方式Weaving:AOP的实现过程Advice的种类
前置增强(Before)后置增强(AfterReturning)异常增强(AfterThrowing)最终增强(After)环绕增强(Around)面:能否写一个Spring AOP例子?
package com.yunche.springaop.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author yunche * 被织入Aspect的对象--target * @date 2019/04/01 */ @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello world"; } } package com.yunche.springaop.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * 切面Aspect--通用功能的实现,这里用于日志处理 * @author yunche * @date 2019/04/01 */ @Aspect @Component public class RequestLogAspect { private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class); //Pointcut @Pointcut("execution(public * com.yunche.springaop.controller..*.*(..))") public void webLog() { } //前置增强 advice @Before("webLog()") public void doBefore(JoinPoint joinPoint) { //使用日志记录下用户的IP和请求的URL ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = attributes.getRequest(); logger.info("URL: " + request.getRequestURI()); logger.info("IP: " + request.getRemoteAddr()); } //后置增强 advice @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) { //用于处理返回的结果 logger.info("RESPONSE: " + ret); } }当访问http://localhost:8080/hello时,切面AOP的日志模块就会自动记录下相应的信息。
2019-04-01 15:53:57.857 INFO 9704 --- [nio-8080-exec-4] c.y.springaop.aspect.RequestLogAspect : URL: /hello 2019-04-01 15:53:57.858 INFO 9704 --- [nio-8080-exec-4] c.y.springaop.aspect.RequestLogAspect : IP: 0:0:0:0:0:0:0:1 2019-04-01 15:53:57.858 INFO 9704 --- [nio-8080-exec-4] c.y.springaop.aspect.RequestLogAspect : RESPONSE: Hello world面:你知道AOP是怎么实现的吗?
答:AOP的实现有:JdkProxy和Cglib(通过修改字节码)
由AOPProxyFactory根据AdvisedSupport对象的配置来决定默认策略如果目标是接口,则用JdkProxy来实现,否则用后者JdkProxy的核心:InvocationHandler和Proxy类Cglib:以继承的方式动态生成目标类的代理JDKProxy:通过Java的内部反射机制实现Cglib:借助ASM(动态修改字节码的框架)反射机制在生成类的过程中比较高效ASM在生成类之后的执行过程中比较高效Spring里的代理模式(接口+真实实现类+代理类)的实现
真实实现类的逻辑包含在了getBean方法里getBean方法返回的实际上是Proxy的实例Proxy实例是Spring采用JDK Proxy或CGLIB动态生成的面:能否写一个通过动态代理的方式实现AOP?
package com.yunche.aop.aspect; /** * @ClassName: MyAspect * @Description: 切面 * @author: yunche * @date: 2018/10/09 */ public class MyAspect { public void start() { System.out.println("模拟事务处理功能..."); } public void end() { System.out.println("程序结束后执行此处..."); } } package com.yunche.aop.jdk; /** * @ClassName: UserDao * @Description: * @author: yunche * @date: 2018/10/09 */ public interface UserDao { void addUser(); } package com.yunche.aop.jdk; /** * @ClassName: UserDaoImpl * @Description: * @author: yunche * @date: 2018/10/09 */ public class UserDaoImpl implements UserDao { @Override public void addUser() { System.out.println("新增用户"); } } package com.yunche.aop.jdk; import com.yunche.aop.aspect.MyAspect; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @ClassName: JdkProxy * @Description: JDK代理类 * @author: yunche * @date: 2018/10/09 */ public class JdkProxy implements InvocationHandler { /** * 声明目标类接口 */ private UserDao userDao; /** * 创建代理方法 * * @param userDao * @return */ public Object createProxy(UserDao userDao) { this.userDao = userDao; //1.类加载器 ClassLoader classLoader = JdkProxy.class.getClassLoader(); //2.被代理对象实现的所有接口 Class[] clazz = userDao.getClass().getInterfaces(); //3.使用代理类、进行增强,返回的是代理后的对象 return Proxy.newProxyInstance(classLoader, clazz, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //声明切面 MyAspect myAspect = new MyAspect(); //指定位置程序执行前执行这个方法 myAspect.start(); //在目标类上调用方法 Object obj = method.invoke(userDao, args); //指定位置程序执行结束后执行 myAspect.end(); return obj; } } package com.yunche.aop.test; import com.yunche.aop.jdk.JdkProxy; import com.yunche.aop.jdk.UserDao; import com.yunche.aop.jdk.UserDaoImpl; /** * @ClassName: JdkTest * @Description: * @author: yunche * @date: 2018/10/09 */ public class JdkTest { public static void main(String[] args) { //创建代理对象 JdkProxy jdkProxy = new JdkProxy(); //创建目标对象 UserDao userDao = new UserDaoImpl(); //从代理对象中获取增强后的目标对象 UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao); //执行方法 userDao1.addUser(); }/*Output: 模拟事务处理功能... 新增用户 程序结束后执行此处... */ }