初学AOP动态代理一些小总结

个人感觉AOP动态代理像在最开始的类上创建了一个替身,以最初类的对象为原型,加了一些个人的方法,稍作修改使之变成了一个新的对象,用这个新的对象去做一些操作。甚至可以创建对多代理类,用哪个的时候加载即可。

有spring Aop的动态代理 和AspectJ两种动态代理方式

一、JDK动态代理

JDK动态代理必须实现接口
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

只要:

commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar

public interface UserDao {

	public void addUser();
	
	public void deleteUser();
}
//需要增强的类
public class UserDaoImpl implements UserDao {

	@Override
	public void addUser() {
		System.out.println("添加用户");
	}

	@Override
	public void deleteUser() {
		System.out.println("删除用户");
	}

}
//切面类
public class MyAspect {

	public void check_Permissions() {
		System.out.println("模拟检查权限");
	}
	public void log() {
		System.out.println("记录日志");
	}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy implements InvocationHandler {

	private UserDao userDao;
	public Object createProxy(UserDao userDao) {
		this.userDao=userDao;
		//类加载器
		ClassLoader classLoader = JdkProxy.class.getClassLoader();
		//返回userDao的所有接口
		Class<?>[] clazz = userDao.getClass().getInterfaces();
		/*
		proxy.newProxyInstance(ClassLoader,Class<?>,InvocationHandler)
        InvocationHandler这个参数应该是下方invoke
        */
		return Proxy.newProxyInstance(classLoader, clazz, this);
	}
	@Override
	/*
	proxy:被代理后的对象,也就是那个小号
	Method:将要被执行的方法
	*/
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		MyAspect myAspect=new MyAspect();
		myAspect.check_Permissions();
		//调用userDao的所有方法,反射应用
		Object obj=method.invoke(userDao, args);
		myAspect.log();
		return obj;
	}

}
public class JdkProxyText {

	public static void main(String[] args) throws ClassNotFoundException {
       UserDao userDao=new UserDaoImpl();
       JdkProxy jdkProxy=new JdkProxy();
       //创造这个代理对象
       UserDao userDao1=(UserDao)jdkProxy.createProxy(userDao);
       userDao1.addUser();
       userDao1.deleteUser();
	}
}

在这里插入图片描述

二、CGLIB动态代理

CGLIB动态代理可以不需要实现接口。

CGLIB动态代理是一个高性能开源的代码生成包,采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

所需jar包同上

//需要增强的类
public class UserService {

	public void goSchool() {
		System.out.println("我想去上学");
	}
}
//切面类
public class MyAspect {

	public void think() {
		System.out.println("你想去学吗?");
	}
	public void fur() {
		System.out.println("不,你不想。");
	}
}
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {

	public Object createProxy(Object obj) {
		// 创建一个动态类对象
		Enhancer enhancer = new Enhancer();
		// 设置enhancer的父类
		enhancer.setSuperclass(obj.getClass());
		// 添加enhancer的回调函数
		enhancer.setCallback(this);
		// 返回创建的代理类
		return enhancer.create();
	}

	/*
	 * 这是一个拦截器
	 * arg0:CGLIB根据父类生成的代理对象
	 * arg1:拦截的方法
	 * arg3:方法的代理对象,用户执行父类的方法
	 */
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		MyAspect myAspect = new MyAspect();
		myAspect.think();
		Object obj = arg3.invokeSuper(arg0, arg2);
		myAspect.fur();
		return obj;
	}

}
public class CglibProxyTest {

	public static void main(String[] args) {
		CglibProxy cglibProxy=new CglibProxy();
		UserService userService=new UserService();
		UserService userService1=(UserService)cglibProxy.createProxy(userService);
		userService1.goSchool();
	}
}

在这里插入图片描述

三、ProxyFactoryBean

FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。
需要:

commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
新增:
spring-aop-4.3.6.RELEASE.jar
aopalliance-1.0.jar

public class CustomerDao {

	public void buy() {
		System.out.println("你想买点什么?");
	}
}
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

//注意此处MethodInterceptor导入的包与CGLIB导入的不同
public class MyAspect implements MethodInterceptor {

	public void welcome() {
		System.out.println("大爷来玩啊(猥琐表情.jpg)");
	}

	public void bye() {
		System.out.println("大爷有空再来啊");
	}

	@Override
	// 这里有点环绕式通知的
	public Object invoke(MethodInvocation arg0) throws Throwable {
		// 前置增强:在原来方法前
		welcome();
		// 执行最初类方法
		Object obj = arg0.proceed();
		// 后置增强:在原来方法前
		bye();
		return obj;
	}

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 目标类 -->
	<bean id="customer"
		class="com.xingrenhaofa.ProxyFactoryBean.CustomerDao" />

	<!-- 切面类 -->
	<bean id="myAspect"
		class="com.xingrenhaofa.ProxyFactoryBean.MyAspect" />

	<!-- 定义一个代理对象 -->
	<bean id="customerProxy"
		class="org.springframework.aop.framework.ProxyFactoryBean">
	    <!--要是被增强的类有接口,下面这条就要写了-->
		<!--  <property name="proxyInterfaces" value="com.xingrenhaofa.ProxyFactoryBean.CustomerDao"></property>-->
		<!-- 指定目标对象 -->
		<property name="target" ref="customer" />
		<!-- 指定切面 -->
		<property name="interceptorNames" value="myAspect" />
		<!-- 指定代理方式 -->
		<property name="proxyTargetClass" value="true" />
	</bean>

</beans>
public class ProxyFactoryBeanTest {

	public static void main(String[] args) {
		String xmlPath = "com/xingrenhaofa/ProxyFactoryBean/file.xml";
		ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
		CustomerDao customer = (CustomerDao) context.getBean("customerProxy");
		customer.buy();
	}
}

在这里插入图片描述

Spring有如下几种通知类型:

  • org.aopalliance.intercept.MethodIntercepter(环绕通知)
    在目标方法执行前后实施增强,可以用于日志、事务管理等功能

  • org.springframework.aop.MethodBeforeAdvice(前置通知)
    在目标方法执行前实施增强,可以用于权限管理等功能

  • org.springframework.aop.AfterReturningAdvice(后置通知)
    在目标方法执行后实施增强,可以用于关闭流、上传文件、删除临时文件等功能

  • org.springframework.aop.ThrowsAdvice(异常抛出通知)
    在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能

  • org.springframework.aop.IntroductionInterceptor(引介通知)
    在目标类中添加一些新的方法和属性,可以应用于修改老版本程序

四、AspectJ代理

commons-logging-1.2.jar
spring-beans-4.3.6.RELEASE.jar
spring-context-4.3.6.RELEASE.jar
spring-core-4.3.6.RELEASE.jar
spring-expression-4.3.6.RELEASE.jar
spring-aop-4.3.6.RELEASE.jar
aopalliance-1.0.jar
新增:
spring-aspects-4.3.6.RELEASE.jar
aspectjweaver-1.8.10.jar

1.基于XML声明式AspectJ
public interface UserDao {

	public void add();

	public void delete();
}
public class UserDaoImpl implements UserDao {
	public void add() {
		System.out.println("添加用户");
	}

	public void delete() {
		System.out.println("删除用户");
	}
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {

	public void before(JoinPoint joinPoint) {
		System.out.println("前置通知");
	}
	public void afterReturn(JoinPoint joinPoint) {
		System.out.println("后置通知");
	}
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("环绕通知开始:");
		Object obj=proceedingJoinPoint.proceed();
		System.out.println("环绕通知结束");
		return obj;
	}
	public void afterThrow(JoinPoint joinPoint,Throwable e) {
		System.out.println("异常通知"+e.getMessage());
	}
	public void after() {
		System.out.println("最终通知");
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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.xsd">

	<bean id="userDao" class="com.xingrenhaofa.proxyXml.UserDaoImpl" />

	<bean id="myAspect" class="com.xingrenhaofa.proxyXml.MyAspect" />

	<aop:config>
	<!--这里定义切面,也就是那个定义很多方法的类-->
		<aop:aspect ref="myAspect">
		    <!--定义切入点,execution内格式为:修饰符(可不写) 返回类型 目标方法的类路径(可不写) 使用的方法 参数-->
		   	<aop:pointcut id="myPointCut"
				expression="execution(* com.xingrenhaofa.proxyXml.*.*(..))" />
			<aop:before method="before" pointcut-ref="myPointCut" />
			<aop:after-returning method="afterReturn"
				pointcut-ref="myPointCut" returning="aa" />
		    <aop:around method="around" pointcut-ref="myPointCut"/>
			<aop:after-throwing method="afterThrow"
				pointcut-ref="myPointCut" throwing="e" />
			<aop:after method="after" pointcut-ref="myPointCut" />
		</aop:aspect>
	</aop:config>
</beans>
public class XmlTest {

	public static void main(String[] args) {
		String xmlPath="com/xingrenhaofa/proxyXml/NewFile.xml";
		ApplicationContext context=new ClassPathXmlApplicationContext(xmlPath);
		UserDao userDao=(UserDao)context.getBean("userDao");
		userDao.add();
		userDao.delete();
	}
}

在这里插入图片描述

2、基于注解的声明式AspectJ

代码和上面有点像,只贴出来不同地方

@Repository("userDao")
public class UserDaoImpl implements UserDao {
	public void add() {
		System.out.println("添加用户");
	}

	public void delete() {
		System.out.println("删除用户");
	}
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {
	// 定义切入点表达式
	@Pointcut("execution(* com.xingrenhaofa.annotation.*.*(..))")
	private void myPointCut() {

	}

	@Before("myPointCut()")
	public void before(JoinPoint joinPoint) {
		System.out.println("前置通知");
	}

	@AfterReturning("myPointCut()")
	public void afterReturn(JoinPoint joinPoint) {
		System.out.println("后置通知");
	}

	@Around("myPointCut()")
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("环绕通知开始:");
		Object obj = proceedingJoinPoint.proceed();
		System.out.println("环绕通知结束");
		return obj;
	}

	@AfterThrowing(value = "myPointCut()", throwing = "e")
	public void afterThrow(JoinPoint joinPoint, 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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 启动注解的声明式AspectJ支持 -->
    <aop:aspectj-autoproxy/>
    <!-- 指定需要扫描的包 -->
    <context:component-scan base-package="com.xingrenhaofa.annotation"></context:component-scan>
        
</beans>

结果也与基于XML声明式AspectJ一样

有点长,,,,就到这里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值