欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring AOP

程序员文章站 2022-07-12 14:32:35
...

3、AOP(Aspect Oriented Programming):面向方面的编程

3.1、静态代理

    当需要为一些成熟的项目增加日志时,需要修改到原有代码,此时可以为原有的对象创建一个静态代理
    类,在这个代理类中实现相应的日志或者权限控制操作。

Spring AOP

    使用静态代理带来的最大问题,需要为每一个类都加入相应的控制代码,这些代码其实是和业务逻辑没有
    关系的。

3.2、动态代理

    动态代理可以将一些横切性的问题(日志管理、权限控制)提取出来成为一个模块,之后在运行的时候根
    据需要切入到原有正常代码中。

    步骤:

    1、创建动态代理类

    创建一个类实现InvocationHandler接口。以后创建对象就完全通过这个代理类进行创建。

    2、在代理类创建相应的要代理的对象

/**
 * 1、写一个实现InvocationHandler接口
 * @author PM
 *
 */
public class LogProxy implements InvocationHandler {
	private LogProxy() {}
	//2、创建一个代理对象
	private Object target;

    3、创建一个静态的getInstance方法来创建代理对象

	//3、创建一个方法来生成对象,这个方法的参数是要代理的对象,getInstance所返回的对象就是代理对象
	public static Object getInstance(Object o) {
		//3.1、创建LogProxy对象
		LogProxy proxy = new LogProxy();
		//3.2、设置这个代理对象
		proxy.target = o;
		//3.3、通过Proxy的方法创建代理对象,第一个参数是要代理对象的classLoader,
		//第二个参数是要代理对象实现的所有接口,第三个参数是实现了InvocationHandler的对象
		//此时的result就是一个代理对象,代理的是o
		Object result = Proxy.newProxyInstance(o.getClass().getClassLoader(), 
				o.getClass().getInterfaces(), proxy);
		return result;
	}

    4、创建了代理对象之后,这个代理对象在执行任何方法时都会先执行invoke方法,就可以在这个方法中
    加入控制信息。

	/**
	 * 当有了代理对象之后,不管这个代理对象执行什么方法,都会调用以下的invoke方法
	 * @param proxy是代理对象
	 * @param method表示要执行的方法
	 * @param args表示要执行方法的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//		if(method.getName().equals("add")||method.getName().equals("delete")) {
//			Logger.info("进行了相应的操作");
//		}
		
		//obj表示方法执行之后的返回值
		//method.invoke()通过反射来执行方法
		Object obj = method.invoke(target, args); 
		//函数执行完成之后返回之后才执行
		if(method.isAnnotationPresent(LogInfo.class)) {
			LogInfo li = method.getAnnotation(LogInfo.class);
			Logger.info(li.value());
		}
		return obj;
	}

    5、根据以上步骤之后,日志管理就被提取成为一个独立的模块,在运行时进行加入。

    6、由于使用的getInstance这个static的方法注入对象,所以无法使用Annotation,需要使用xml的配置文
    件。

	<bean id="userDynamicDao" class="org.pm.spring.proxy.LogProxy" factory-method="getInstance">
		<constructor-arg ref="userDao"/>
	</bean>
	
	<!-- factory-method="getInstance":创建对象的静态方法 -->
	<bean id="messageDynamicDao" class="org.pm.spring.proxy.LogProxy" factory-method="getInstance">
		<!-- 为getInstance这个静态方法传入参数,传入的参数是什么类型,
			代理出来的对象就是什么类型 -->
		<constructor-arg ref="messageDao"/>
	</bean>

   service中通过代理类注入对象:

	@Resource(name="messageDynamicDao")
	public void setMessageDao(IMessageDao messageDao) {
		this.messageDao = messageDao;
	}

	public IUserDao getUserDao() {
		return userDao;
	}
	
	//默认通过名称注入,在JSR330中提供了@Inject来注入
	@Resource(name="userDynamicDao")
	public void setUserDao(IUserDao userDao) {
		this.userDao = userDao;
	}

    7、此时可以考虑为相应的方法增加Annotation来增强相应的操作。

    7.1、创建Annotation:

@Retention(RetentionPolicy.RUNTIME)
public @interface LogInfo {
	public String value() default"";
}

    7.2、为接口方法增加Annotation:

public interface IUserDao {
	@LogInfo("添加用户信息")
	public void add(User user);
	@LogInfo("删除用户信息")
	public void delete(int id);
	public User load(int id);
}

3.3、使用Spring实现AOP

    Spring就是使用动态代理来实现AOP的。

   1、 基于Annotation的实现:

    1.1、在xml中打开aop的自动检索

	<!-- 打开基于Annotation的AOP -->
	<aop:aspectj-autoproxy/>

    1.2、创建一个需要实现动态代理的类

@Component("logAspect") //让这个切面类被Spring所管理
@Aspect //声明这个类是一个切面类
public class LogAspect {
	
	/**
	 * execution(* org.pm.spring.dao.*.add*(..))
	 * 第一个*表示任意返回值
	 * 第二个*表示org.pm.spring.dao包中的所有类
	 * 第三个*表示以add开头的所有方法
	 * (..)表示任意参数
	 */
	@Before("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logStart(JoinPoint jp) {
		//得到执行的对象
		System.out.println(jp.getTarget());
		//得到执行的方法
		System.out.println(jp.getSignature().getName());
		Logger.info("加入日志");
	}
	/**
	 * 函数调用完成之后执行
	 * @param jp
	 */
	@After("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logEnd(JoinPoint jp) {
		Logger.info("方法调用结束加入日志");
	}
	/**
	 * 函数调用中执行
	 * @param pjp
	 * @throws Throwable
	 */
	@Around("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logAround(ProceedingJoinPoint pjp) throws Throwable {
		Logger.info("开始在Around中加入日志");
		pjp.proceed(); //执行程序
		Logger.info("结束Around");
	}

    1.3、导入aspect包,spring使用的是AspectJ这个包来实现AOP,所以需要导入这个包。

Spring AOP

    1.4、在这个类上面使用@Aspect来声明这个类是一个切面类

@Component("logAspect") //让这个切面类被Spring所管理
@Aspect //声明这个类是一个切面类
public class LogAspect {

    1.5、在相应的要进行操作横切点上面加入PointCut的说明

	/**
	 * execution(* org.pm.spring.dao.*.add*(..))
	 * 第一个*表示任意返回值
	 * 第二个*表示org.pm.spring.dao包中的所有类
	 * 第三个*表示以add开头的所有方法
	 * (..)表示任意参数
	 * 可以使用||来加入多个条件
	 */
	@Before("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logStart(JoinPoint jp) {
		//得到执行的对象
		System.out.println(jp.getTarget());
		//得到执行的方法
		System.out.println(jp.getSignature().getName());
		Logger.info("加入日志");
	}

    1.6、如果希望获取相应的调用信息,可以通过JoinPoint这个参数进行传递

	public void logStart(JoinPoint jp) {
		//得到执行的对象
		System.out.println(jp.getTarget());
		//得到执行的方法
		System.out.println(jp.getSignature().getName());
		Logger.info("加入日志");
	}

    1.7、其他知识

	/**
	 * 函数调用完成之后执行
	 * @param jp
	 */
	@After("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logEnd(JoinPoint jp) {
		Logger.info("方法调用结束加入日志");
	}
	/**
	 * 函数调用中执行
	 * @param pjp 一定要传入ProduceJoinPoint,通过这个参数数据才能往下走
	 * @throws Throwable
	 */
	@Around("execution(* org.pm.spring.dao.*.add*(..))||"
			+ "execution(* org.pm.spring.dao.*.delete*(..))||"
			+ "execution(* org.pm.spring.dao.*.update*(..))")
	public void logAround(ProceedingJoinPoint pjp) throws Throwable {
		Logger.info("开始在Around中加入日志");
		pjp.proceed(); //执行程序
		Logger.info("结束Around");
	}

    2、基于xml的实现:

    使用Annotation需要在大量的joinpoint上加入execution,感觉不是很方便,使用xml会相对简单一些。

    2.1、创建一个需要实现动态代理的类:

@Component("logAspect") //让这个切面类被Spring所管理
public class LogAspect {
	
	public void logStart(JoinPoint jp) {
		//得到执行的对象
		System.out.println(jp.getTarget());
		//得到执行的方法
		System.out.println(jp.getSignature().getName());
		Logger.info("加入日志");
	}

	public void logEnd(JoinPoint jp) {
		Logger.info("方法调用结束加入日志");
	}

	public void logAround(ProceedingJoinPoint pjp) throws Throwable {
		Logger.info("开始在Around中加入日志");
		pjp.proceed(); //执行程序
		Logger.info("结束Around");
	}

    2.2、导入aspect包,配置xml文件:

	<aop:config>
	<!-- 定义切面,ref="logAspect"表示代理类中定义的切面对象的引用 -->
		<aop:aspect id="myLogAspect" ref="logAspect">
		<!-- 在哪些位置加入相应的Aspect -->
			<aop:pointcut id="logPointCut" expression="execution(* org.pm.spring.dao.*.add*(..))||
				execution(* org.pm.spring.dao.*.delete*(..))||
				execution(* org.pm.spring.dao.*.update*(..))" />
			<aop:before method="logStart" pointcut-ref="logPointCut"/>
			<aop:after method="logEnd" pointcut-ref="logPointCut"/>
			<aop:around method="logAround" pointcut-ref="logPointCut"/>
		</aop:aspect>
	</aop:config>

 

转载于:https://my.oschina.net/pmos/blog/785898