讲到java企业级开发框架,就不可避免的讲到 IOC,AOP,MCV
今天面试时被问到AOP,讲的很乱,这里整理笔记,包括AOP,spring-AOP的部分知识,错误的地方请小伙伴指出来.
谈谈你对AOP的理解:
AOP概念(Aspect-Oriented Programming):
AOP原理:
AOP术语:
使用AOP框架时,开发者主要工作为定义点和增强,通常采用XML配置文件或者注解的方式,配置好增强的信息和切入点后,AOP框架会自动生成AOP代理。
————————————————
在java语言的编写的程序中,从源代码到最终运行,会经历编写源代码,编译出字节码,加载字节码,运行程序几个阶段。各个阶段都可以以特定的方式织入增强。
JDK动态代理是java.lang.reflect.*包提供的方式(反射)。必须借助接口才能在运行期生成代理对象,对于使用业务接口的类,Spring默认使用JDK动态代理实现AOP。不需要引入第三方包,不能针对类。只要一个类实现了某个接口,就可以通过动态代理机制在运行期动态的构造这个接口的实现对象。
按照JavaSE动态代理的要求:
1,需要编写一个类实现java.lang.reflect.InvocationHandler接口。实现java.lang.reflect.InvocationHandler接口时需要重写invoke()方法:
Object invoke(Object proxy,Method method,Object[] args)throws Exception; //proxy :自动生成的动态代理对象,与目标对象会实现同一接口; //method:运行时调用的方法,此方法应为指定接口中定义的方法。 //args:调用method方法被调用时的返回值。 //返回值:代理对象的method方法被调用时的返回值,
2,构造上述实现类的实例,然后调用java.lang.Proxy的newProxyInstance()方法获取自动生成的代理对象,完成业务逻辑的方法调用。
public static Object newProxyInstance( ClassLocader loader,Class<?>[] interfaces,InvocationHandler handler) throws IllegalArgumentException //loader:代理类的加载器 //interfaces:代理类的所有接口,这些接口中的方法都会被拦截。 //handler:动态代理的对象。 //返回值:动态生成代理对象,此对象会实现inferface参数中包括的所有接口。
业务接口及实现类
package dynamic.jdk; public interface TestDao { public void save(); public void modify(); public void delete(); }
package dynamic.jdk; public class TestDaoImpl implements TestDao { public TestDaoImpl() { // TODO Auto-generated constructor stub } @Override public void save() { // TODO Auto-generated method stub System.out.println("保存"); } @Override public void modify() { // TODO Auto-generated method stub System.out.println("修改"); } @Override public void delete() { // TODO Auto-generated method stub System.out.println("删除"); } } 代理类:
代理类:
package dynamic.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import aspect.Myspect; public class JDKDynamicPorxy implements InvocationHandler { //声明目标类对象真实对象) private TestDao testdao; //创建代理对象,建立代理对象和真实对象的代理关系,并返回代理对象。 public Object createProxy(TestDao testDao) { this.testdao=testDao; //获取代理类 的构造器 ClassLoader cld = JDKDynamicPorxy.class.getClassLoader(); //获取目标类的所有接口 Class [] clazz = testdao.getClass().getInterfaces(); //动态生成代理对象,需要代理类的类构造器,目标类所有接口,即目标类实例。 return Proxy.newProxyInstance(cld, clazz, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub //切面对象 Myspect myAspect = new Myspect(); //调用切面方法 myAspect.check(); myAspect.except(); //执行目标类方法(被增强的方法)对应的实例对象的该method 方法。 Object obj = method.invoke(testdao, args); myAspect.log(); myAspect.monitor(); return obj; } }
测试类:
package dynamic.jdk; public class JDKDynamicTest { public static void main(String[] args) { // TODO Auto-generated method stub //实例化代理类 JDKDynamicPorxy jdkdynamicProxy = new JDKDynamicPorxy(); //实例化目标类 TestDao testdao = new TestDaoImpl(); //生成代理对象 TestDao testDaoAdvice = (TestDao)jdkdynamicProxy.createProxy(testdao); testDaoAdvice.save(); System.out.println("++++++"); testDaoAdvice.modify(); System.out.println("++++++"); testDaoAdvice.delete(); } }
JDK动态代理必须要提供接口才可以使用,并且增强的方法只能是接口中申明的方法,对于没有提供接口的类,采用动态字节码的方式,CGLIB为流行的动态字节码生成工具,只能采用CGLIB动态代理。
目标类
package dynamic.cglib; public class TestDao { public void svae() { System.out.println("保存"); } public void modify() { System.out.println("修改"); } public void delete() { System.out.println("删除"); } }
代理类:
package dynamic.cglib; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import aspect.Myspect; public class CglibDynamicProxy implements MethodInterceptor{ /* * 创建代理类的方法,生成CGLIb代理对象。 * target是目标对象,需要增强的对象, * 返回目标对象的CGLIB代理对象。 */ public Object createProxy(Object target) { //创建一个动态类对象,即增强类对象, Enhancer enhancer = new Enhancer(); //确定要增强的类,设置其父类, enhancer.setSuperclass(target.getClass()); //确定代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor的方法。 enhancer.setCallback(this); //返回创建的代理对象。 return enhancer.create(); } /* * intercept方法会在程序执行目标方法时被调用, * proxy是CGLIB根据指定的父类生成的代理对象。 * method是拦截器 * args是拦截器方法的参数组 * methodProxy是方法的代理对象,用于执行父类的方法 * 返回代理结果 * (non-Javadoc) * @see org.springframework.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], org.springframework.cglib.proxy.MethodProxy) */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // TODO Auto-generated method stub //创建一个切面 Myspect myaspect = new Myspect(); myaspect.check(); myaspect.except(); Object obj = methodProxy.invokeSuper(proxy, args); //后增强 myaspect.log(); myaspect.monitor(); return obj; } }
测试类
package dynamic.cglib; public class CGLIBDynamicTest { public static void main(String [] args) { //创建代理对象 CglibDynamicProxy cdb = new CglibDynamicProxy(); //创建目标类对象 TestDao testDao = new TestDao(); //获取增强后的目标对象 TestDao testDaoAdvice = (TestDao)cdb.createProxy(testDao); //执行方法 testDaoAdvice.svae(); testDaoAdvice.modify(); testDaoAdvice.delete(); } }
Spring Aop
Spring框架通过Java SE动态代理和cglib实现了AOP功能:当明确指定目标类的实现的业务接口时,Spring使用动态代理的方式,也可以强制使用cglib,没有指定目标类的接口时,Spring使用cglib进行字节码增强
增强(通知)的类型:根据Spring中通知在目标类中方法中的连接点的位置,分为6种类型。
————————————————
ProxyFactoryBean:ProxyFactoryBean是org.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他bean实例创建代理实例。
ProxyFactoryBean类的常用属性
切面类
package spring.proxyfactorybean; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /* * 切面类 * */ public class Myaspect implements MethodInterceptor { public Myaspect() { // TODO Auto-generated constructor stub } @Override public Object invoke(MethodInvocation arg0) throws Throwable { // TODO Auto-generated method stub //增强方法 check(); except(); //执行目标方法; Object obj = arg0.proceed(); //增强方法 log(); moitor(); return obj; } private void moitor() { // TODO Auto-generated method stub System.out.println("性能检测!!"); } private void log() { // TODO Auto-generated method stub System.out.println("模拟日志记录!!"); } private void except() { // TODO Auto-generated method stub System.out.println("模拟异常处理"); } private void check() { // TODO Auto-generated method stub System.out.println("模拟权限控制!!"); } }
配置切面并 指定代理:
<!-- 定义目标对象 --> <bean id = "testDao" class = "dynamic.jdk.TestDaoImpl"/> <!-- 创建一个切面 --> <bean id = "myAspect" class = "spring.proxyfactorybean.Myaspect"/> <!-- 使名用Spring代理工厂定义一个 代理对象--> <bean id = "testProxy" class = "org.springframework.aop.framework.ProxyFactoryBean"> <!-- 指定代理实现的接口 --> <property name = "proxyInterfaces" value = "dynamic.jdk.TestDao"/> <!-- 指定目标对象 --> <property name = "target" ref = "testDao"/> <!-- 指定切面 ,织入环绕增强--> <property name = "interceptorNames" value = "myAspect"/> <!-- 指定代理方式 --> <property name = "proxyTargetClass" value = "true"/> </bean>
创建测试类:
package spring.proxyfactorybean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import dynamic.jdk.TestDao;
public class ProxyFactoryBeanTest {
public ProxyFactoryBeanTest() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext12.xml");
//从容器中获得增强后的目标对象
System.out.println(applicationContext.getBean("testProxy").getClass().getName());
TestDao testdaoAdvice =(TestDao)applicationContext.getBean("testProxy");
//执行方法
testdaoAdvice.save();
System.out.println("++++++++++++++++++++");
testdaoAdvice.modify();
System.out.println("++++++++++++++++++++");
testdaoAdvice.delete();
}
}
————————————————
AspectJ是一个基于Java语言的AOP框架,从Spring2.0后引入AspectJ的支持,建议使用AspectJ框架实现SpringAOP,有两种方式,一是基于XML配置开发AspectJ,二是基于注解开发AspectJ。
Spring配置文件中的<beans>元素可以包含多个<aop:config>,一个<aop:config>元素又可以包含属性和子元素,子元素包括<aop:pointcut>、<aop:advisor>、<aop:aspect>,配置时,这3个子元素必须按照此顺序定义。
1.1配置切面
在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素可以将定义好的Spring Bean转换成切面Bean,所以先定义一个普通的Spring Bean。配置<aop:aspect>元素时,通常会指定id和ref两个属性。
id用来定义该切面的唯一标识名称。 ref用于引用普通的Spring Bean。
1.2配置切入点
当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。
定义<aop:pointcut>元素时,通常会指定id和expression两个属性,id用于指定切入点的唯一标识名称。 expression用于指定切入点关联的切入点表达式。
1.3配置通知
使用<aop:aspect>的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:
————————————————
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!-- 定义目标类对象 --> <bean id = "testDao" class = "dynamic.jdk.TestDaoImpl"/> <!-- 配置切面 --> <bean id = "myaspect" class = "aspect.xml.MyAspect"/> <!-- AOP配置 --> <aop:config> <!-- 配置切面 --> <aop:aspect ref = "myaspect"> <!-- 配置切入点,通知增强那些方法 --> <aop:pointcut expression = "execution(* dynamic.jdk.*.*(..))" id = "myPointCut"/> <!-- 将通知与切入点关联 --> <!-- 关联前置切入点 --> <aop:before method = "before" pointcut-ref = "myPointCut"/> <!-- 关联后置增强点 --> <aop:after-returning method ="afterReturning" pointcut-ref = "myPointCut"/> <!-- 关联环绕增强点 --> <aop:around method = "around" pointcut-ref = "myPointCut"/> <!-- 关联异常通知 --> <aop:after-throwing method = "except" pointcut-ref = "myPointCut"/> <!-- 关联后置(最终)通知,不管目标方法是否执行都要执行 --> <aop:after method ="after" pointcut-ref = "myPointCut"/> </aop:aspect> </aop:config> </beans>
注解名称解释
————————————————
@Aspect //对应<aop:aspect ref="myAspect"> @Component //对应<bean id="myAspect" class=""> public class MyAspect { /** * 定义切入点 @Pointcut 相当于 <aop:pointcut id="myPointCut" expression="execution(* edu.njxz.demo.dynamicProxy.*.*(..))"></aop:pointcut> */ @Pointcut("execution(* edu.njxz.demo.dynamicProxy.*.*(..))") private void myPointCut(){ } /** * 前置通知,使用JoinPoint接口作为参数获得目标对象信息 * @param jp */ @Before("myPointCut()") public void before(JoinPoint jp){ System.out.print("前置通知:模拟权限控制"); System.out.println(",目标对象:"+jp.getTarget()+" ,被增强处理的方法:"+jp.getSignature().getName()); } /** * 后置返回通知 * @param jp */ @AfterReturning("myPointCut()") public void afterReturning(JoinPoint jp){ System.out.print("后置返回通知:模拟删除临时文件"); System.out.println(" ,被增强处理的方法:"+jp.getSignature().getName()); } /** * 环绕通知 * @param pjp * @return * @throws Throwable * 必须throws Throwable */ @Around("myPointCut()") public Object around(ProceedingJoinPoint pjp) throws Throwable{ //开始 System.out.println("环绕开始:执行目标方法前,模拟开启事务"); //执行当前目标方法 Object obj=pjp.proceed(); //结束 System.out.println("环绕结束:执行目标方法后,模拟关闭事务"); return obj; } /** * 异常通知 * */ @AfterThrowing(value = "myPointCut()",throwing = "e") public void except(Throwable e){ System.out.println("异常通知:"+"程序执行异常"+e.getMessage()); } /** * 后置通知,(最终通知) */ @After("myPointCut()") public void after(){ System.out.println("最终通知:模拟释放资源"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 指定需要扫描的包,是注解生效--> <context:component-scan base-package="edu.njxz.demo.aspectAnnotation"></context:component-scan> <context:component-scan base-package="edu.njxz.demo.dynamicProxy"></context:component-scan> <!-- 启动基于注解的AspectJ支持--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
原文:https://www.cnblogs.com/liruilong/p/12416995.html