AOP总结

				                          **Spring-AOP**

1.Spring-AOP理论:
aop 是软件设计领域中的面向切面编程,它是面向对象(oop)编程的一种补充和完善
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。)

2.AOP的作用:
AOP就是要在基于OCP(开闭原则)在不改变原有系统核心业务代码的基础上,动态添加一些扩展功能并可以控制对象的执行。

3.AOP实际项目的应用场景:
AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。

4.AOP底层原理分析:
AOP底层基于代理机制实现功能扩展:(了解)

  1. 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
    如图(1):
    图片绿色的都为底层原理及调用
    AOP总结

1.1)代码步骤:
(1.1.1)在spring框架整合中的核心配置文件中(我的配置文件名叫spring-configs.xml)配置一行:

<!--启用Aop  -->
<aop:aspectj-autoproxy/>

(1.1.2)spring-configs.xml中的代码为:

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd">
   
       <!-- 指定spring 框架要扫描的包结构 -->
       <context:component-scan base-package="com.db"/>
       <!-- 加载configs.properties :
            1)系统底层会读取配置文件configs.properties
            2)系统底层会将配置文件内容封装到Properties对象
            3)系统底层将Properties对象交给Spring容器进行管理-->
       <util:properties id="cfg" 
             location="classpath:configs.properties"/>
       <!-- 加载spring-model.xml(此配置文件中要配置
            service,dao,datasource,mybatis) -->
       <import resource="classpath:spring-model.xml"/>
       <import resource="classpath:spring-shiro.xml"/>
       <!-- 加载spring-web.xml -->
       <import resource="classpath:spring-web.xml"/>
       
       ******<!--启用Aop 切面-->
       <aop:aspectj-autoproxy/>******
</beans>

(1.1.3)自定义一个注解后面用,这里根据你自己的想法来。不过通过这个注解可以进行细粒度的切入具体类的哪个方法。

package com.db.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/*
 * 自定义注解
 * 1.jdk1.5推出的一种元数据
 * 2.本质上是一个特殊的Class
 * 3.主要用于描述类,方法,属性,参数。
 * 
 * spring中描述bean对象方式
 * xml:<bean id="">
 * 注解:@service,@Controller。。。。
 * 
 * 注解的应用:
 * 1)编译时:@Override...
 * 2)运行时:@Controller...会结合反射一起应用,否则注解无效。
 * 自定义注解:
 * 1)框架中定义
 * 2)自己定义(借助@interface)
 * 
 * 
 * @author A
 *
 */
//可以修饰类,方法:METHOD表示修饰方法
@Target(ElementType.METHOD)
//何时有效 :RUNTIME运行时有效被阿表示
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredLog {
	/*@RequiredLog("123456")
     *String value();
     */
	//@RequiredLog
	String value()default"";

}

(1.1.4)Pointcut:切入点;
@Pointcut(“bean(*ServiceImpl)”)所有类的所有方法,粗粒度
@Pointcut(“bean(sysUserServiceImpl)”)访问那个类中的所有方法
@Pointcut("@annotation(com.db.common.annotation.RequiredLog)")//细粒度的访问哪个类的哪个方法

SysLogAspect这个类中的代码(动态的业务实现,这个类中主要写的是AOP切面的应用):

  /**
     * 日志切面类型(对用户行为进行记录)
     * @author A
     * @Aspect
     *
     */
    @Order(1)
    @Aspect
    @Service
    public class SysLogAspect {
    	@Autowired
    	private SysLogDao sysLogDao;
    	//当方法参数为ProceedingJoinPoint类型时候,并且希望在目标方法之前或者之后都有功能扩展,一般借助于@Around进行修饰
    	@Pointcut("@annotation(com.db.common.annotation.RequiredLog)")
    	public void  doLongpoint(){}
    	
    	@Around("doLongpoint()")
    	public Object around(ProceedingJoinPoint jp)throws Throwable{
    		System.out.println("start:"+System.nanoTime());
    		Object result = jp.proceed();//执行目标方法
    		System.out.println("end:"+System.nanoTime());
    		long t1 = System.currentTimeMillis();
    		long t2 = System.currentTimeMillis();
    		System.out.println("记录用户行为");
    		//将日志信息写入导数据库
    		saveLog(jp,(t2-t1));
    		return result;
    	}
    
    	private void saveLog(ProceedingJoinPoint jp, long time) throws Exception, SecurityException {
    		//获取日志信息
    		MethodSignature s = (MethodSignature) jp.getSignature();
    		Class<?>[]parameterTypes=s.getParameterTypes();
    		System.out.println(parameterTypes.toString());
    		String methodName = s.getName();
    		//获取目标方法对象
    		Class<?>targetClass=jp.getTarget().getClass();
    		Method targetMethod=targetClass.getDeclaredMethod(methodName, parameterTypes);
    		
    		//获取目标方法信息(类名+"."+方法名)
    		String method = jp.getTarget().getClass().getName();
    		//获取方法上的注解
    		RequiredLog requiredLog = targetMethod.getDeclaredAnnotation(RequiredLog.class);
    		//获取注释上指定的操作名
    		String  operation="";
    		if(requiredLog!=null){
    			operation=requiredLog.value();
    		}
    		//获取登录的用户名
    		SysUser user = (SysUser)SecurityUtils.getSubject().getPrincipal();
    		String username = user.getUsername();
    		//封装用户信息
    		SysLog sysLog = new SysLog();
    		//1.9获取ip地址
    		 String ip=IPUtils.getIpAddr();//工具类暂且不需要理解
    		 //2.封装日志信息
    		 SysLog log=new SysLog();
    		 log.setUsername(username);
    		 log.setIp(ip);
    		 log.setOperation(operation);
    		 log.setMethod(method);
    		 log.setParams(Arrays.toString(jp.getArgs()));
    		 log.setTime(time);
    		 log.setCreatedTime(new Date());
    		 //3.持久化日志信息
    		 sysLogDao.insertObject(log);
    	}
    }

(1.1.5)代理类实现接口的 @Before,@After,@AfterReturning,@AfterThrowing5,@Around大注解执行顺序:
代码如下:
`@Order(2)
@Aspect
@Service
public class Otheraspect {
@Pointcut(“bean(SysUserServiceImpl)”)
public void doPointCut(){}

@Before(" doPointCut()")
public void beforeMethod(){
	System.out.println("@Before");
}
@After(" doPointCut()")
public void afterMethod(){
	System.out.println("@After");
}
@AfterReturning(" doPointCut()")
public void returnMethod(){
	System.out.println("@AfterReturning");
}
@AfterThrowing(" doPointCut()")
public void throwMethod(){
	System.out.println("@AfterThrowing");
}
@Around(" doPointCut()")
public Object arroundMethod(ProceedingJoinPoint jp) throws Throwable{

	try {
		System.out.println("@Around before");
		Object obj = jp.proceed();
		System.out.println("@Around afterReturning");
		return obj;
	} catch (Exception e) {
		System.out.println("@Around afterthrowing");
		throw e;
	}finally{
		System.out.println("Around after");
	}
	
}

}`
(1.1.5.1)第一种情况当程序正常运行时:
AOP总结

代码运行结果:AOP总结
(1.1.6.1)第二种情况当程序没有正常运行时:
AOP总结
代码运行结果:AOP总结

  1. 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
    如图(2):
    图片绿色的都为底层原理及调用
    AOP总结

这是一个Dome类测试的TestCglibo1代码如下 :

//目标类
class GuanggaoService{
	public void Service(){
		System.out.println("广告");
	}
}
//CGLIB代理
public class TestCglibo1 {
	public static void main(String[] args) {
		//创建目标对象
		GuanggaoService target = new GuanggaoService();
		
		//2.为目标对象创建代理对象
		//2.1创建Enhancer对象,通过此对象为目标对象创建代理对象
		Enhancer en = new Enhancer();
		en.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodproxy) throws Throwable {
				System.out.println("en:"+en);
				Object result = methodproxy.invokeSuper(obj, args);
				return result;
			}
		});
		//设置代理对象的父类类型
		en.setSuperclass(target.getClass());
		//创建代理对象
		GuanggaoService proxy = (GuanggaoService) en.create();
		//调用代理对象的方法、
		proxy.Service();
	}
}