您的位置:首页 > 编程语言 > Java开发

spring之AOP

2015-06-30 15:12 633 查看

什么是AOP

Aspect Oriented Programing面向切面编程。利用AOP的技术我们可以轻松消除分散在各个模块逻辑代码中的重复代码,可以对我们指定的方法增强,代理等等

aop术语

连接点Joinpoint

程序执行的某个特定的位置,某个方法调用前,某个方法调用后,方法抛出异常后等等

切点pointcut

增强advice

目标Target

需要被增强的目标类

引入 introduction为某个类添加属性或者方法,或者继承某个类

织入weaving

代理proxy

切面aspect

springAop代理机制

jdk自带的动态代理

jdk的动态代理主要用到了Java.lang.reflect包下面的Proxy和InvocationHandler

代理类PlayGameProxy .java

public class PlayGameProxy implements InvocationHandler {
static Logger logger = Logger.getLogger(PlayGameProxy.class);
Object traget;

public PlayGameProxy(Object playGameInterface) {
this.traget = playGameInterface;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
logger.info("打开电脑噢");
Object object = method.invoke(traget, args);
logger.info("关闭电脑");
return object;
}

}


调用类

public class NormalProxyTest {
@Test
public void test() {
PlayGameInterface pg = new StartPlayGame();
PlayGameProxy pgProxy = new PlayGameProxy(pg);
// Proxy.newProxyInstance()最主要是这个方法
PlayGameInterface PlayGaemProxy = (PlayGameInterface) Proxy.newProxyInstance(pg.getClass().getClassLoader(), pg.getClass().getInterfaces(),
pgProxy);
PlayGaemProxy.palyGame();
PlayGaemProxy.palyGame2();
}
}


cglib的动态代理,因为jdk的代理技术必须要有接口类,如果某些类没有接口,就无法实现动态代理了,所以这时候cglib就出场了采用了字节码技术。主要用到了Enhancer类去增强

CglibProxy.java

@Slf4j
public class CglibProxy implements MethodInterceptor {
Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);// 设置子类需要创建的类
enhancer.setCallback(this);
return enhancer.create();// 创建子类实例
}
public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable {
log.info("【1】打开电脑噢");
Object result = proxy.invokeSuper(obj, args);
log.info("【3】关闭电脑");
return result;
}
}


测试类

@Test
public void test() {
CglibProxy cglibProxy = new CglibProxy();
StartPlayGame startPlayGame = (StartPlayGame) cglibProxy.getProxy(StartPlayGame.class);
startPlayGame.palyGame();
startPlayGame.palyGame2();
}


输出结果:

2015-06-30 15:02:43,785 INFO [main] (CglibProxy.java:27) - 【1】打开电脑噢

开始玩游戏了噢

2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:29) - 【3】关闭电脑

2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:27) - 【1】打开电脑噢

开始玩游戏了噢22222

2015-06-30 15:02:43,807 INFO [main] (CglibProxy.java:29) - 【3】关闭电脑

缺点:

如果我们只想给某个类(StartPlayGame)的某个方法(palyGame)加上代理,其他的方法不加代理。使用动态代理是做不到的,动态代理会给目标类的所有方法都加上增强。

如果我们代理一个类就要专门为这个类创建代理过程,编写相关代码,无法做到通用。

所以后面才引入了切点切面的概念!

Aop联盟

创建增强类Advice MethodBeforeAdvice(前置增强)、MethodInterceptor(拦截)、AfterReturningAdvice(后置增强)

其实实现效果就是相当于动态代理技术里面 invoke或者intercept方法,在调用真正方法之前,进行的操作。

通过代码配置增强

@Slf4j
public class PlayGameBeforAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
log.info("【1】方法执行之前");
}
}

public class PlayGameAfterAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
log.info("【3】方法执行之后");
}
}
public class PlayGameInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("【1】环绕增强之前");
invocation.proceed();// 调用反射执行方法
log.info("【3】环绕增强之后");
return null;
}
}


测试

private void testByMethod() {
// 测试前置增强
PlayGameBeforAdvice pba = new PlayGameBeforAdvice();
excuteAdviceByParam(pba);
// 测试后置增强
PlayGameAfterAdvice paa = new PlayGameAfterAdvice();
excuteAdviceByParam(paa);
// 测试环绕增强
PlayGameInterceptor pgi = new PlayGameInterceptor();
excuteAdviceByParam(pgi);
}

public void excuteAdviceByParam(Advice advice) {
try {
PlayGame target = new PlayGameImp();
// spring提供的代理工厂
ProxyFactory proxy = new ProxyFactory();
// 设置代理的接口
proxy.setInterfaces(target.getClass().getInterfaces());
// 设置代理目标
proxy.setTarget(target);
// 为代理目标添加增强
proxy.addAdvice(advice);
// 强制以cglib形式代理 对于单例模式来说第一次创建比较慢,后来就好。
proxy.setOptimize(true);
// 生成代理类
PlayGame play = (PlayGame) proxy.getProxy();
play.playLOL();
} catch (Exception e) {
log.error("测试aop增强失败!:" + e.getLocalizedMessage());
}
}


通过spring配置增强

<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- 前置增强,后置增强,环绕增强 -->

<bean id="target" class="com.team.gaoguangjin.springinaction.springAop.PlayGameImp" />
<bean id="playGameAfterAdvice" class="com.team.gaoguangjin.springinaction.springAop.PlayGameAfterAdvice" />
<bean id="playGameBeforAdvice" class="com.team.gaoguangjin.springinaction.springAop.PlayGameBeforAdvice" />
<bean id="playGameInterceptor" class="com.team.gaoguangjin.springinaction.springAop.PlayGameInterceptor" />

<!-- xml配置aop的关系 -->
<bean id="play" class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyInterfaces="com.team.gaoguangjin.springinaction.springAop.PlayGame"
p:interceptorNames="playGameBeforAdvice" p:target-ref="target"
p:proxyTargetClass="true" p:singleton="true" />
</beans>


测试代码

private void testAopXml() {
try {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/team/gaoguangjin/springinaction/springAop/beans.xml");
PlayGame play = (PlayGame) ac.getBean("play");
play.playLOL();
} catch (Exception e) {
log.error("测试xml aop增强失败!:" + e.getLocalizedMessage());
}
}


基于@Aspectj和schema配置的AOP

aspectj

基于aspect的代码配置aop

AspectDemo.java 切面类

@Aspect
@Slf4j
public class AspectDemo {
@Before("execution(* playLOL(..))")
public void beging() {
log.info("【1】 玩游戏之前需要【打开】电脑 通过方法方式");
}

@After("execution(* playLOL(..))")
public void end() {
log.info("【3】 不想玩游戏睡觉了需要【关闭】电脑 通过方法方式");
}

@Before("execution(* playCs(..))")
public void beging1() {
log.info("【1】 玩游戏之前需要【打开】电脑 通过xml方式");
}

@After("execution(* playCs(..))")
public void end2() {
log.info("【3】 不想玩游戏睡觉了需要【关闭】电脑 通过xml方式");
}

// 拦截所有带了注解的方法
@Before("@annotation(com.team.gaoguangjin.springinaction.annoation.JdkAnnoation)")
public void annationBegin() {
log.info("【1】 注解方法运行之前");
}

@After("@annotation(com.team.gaoguangjin.springinaction.annoation.JdkAnnoation)")
public void annationEnd() {
log.info("【3】 注解方法之后");
}
}


代码调用,当playLOL方法被调用时候,会触发@Before和@After

execution里面是条件

private void getFromMethod() {
PlayGame target = new PlayGameImp();
// 定义 AspectJProxyFactory 而不是ProxyFactory
AspectJProxyFactory aspectFactory = new AspectJProxyFactory();
// 先要setTarget 然后再添加aspect切面
aspectFactory.setTarget(target);
// 添加切面类
aspectFactory.addAspect(AspectDemo.class);
// 生成切面的代理对象
PlayGame proxy = aspectFactory.getProxy();
proxy.playLOL();
}


基于aspect的xml配置aop


<?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-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 
<!--【1】与【2】效果类似,用其中一个就可以了  -->
<!-- 【1】基于切面的驱动器 -->
<aop:aspectj-autoproxy />
<!--http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> 【2】自动代理创建器,
<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>
-->
<bean id="playGame" class="com.team.gaoguangjin.springinaction.aspectAop.PlayGameImp" />
<bean id="asepectDemo" class="com.team.gaoguangjin.springinaction.aspectAop.AspectDemo" />
</beans>


调用测试,因为
<aop:aspectj-autoproxy />
会自动扫描注解需要代理的对象

private void geFromXml() {
try {
ApplicationContext ac = new ClassPathXmlApplicationContext("com/team/gaoguangjin/springinaction/aspectAop/beans.xml");
PlayGame play = (PlayGame) ac.getBean("playGame");
play.playCs();
} catch (Exception e) {
log.error("通过xml方式得到aspect注解的aop失败!" + e.getLocalizedMessage());
}
}


Schema

基于schemd的 aspect配置

增强类切面AdviceMethods .java

public class AdviceMethods {
public void preGreeting(String name) {
System.out.print("所有方法都会触发!--参数为:");
System.out.println(name);
}

// 后置增强对应方法
public void afterReturning(String retVal) {
System.out.println("----afterReturning()----");
System.out.println("returnValue:" + retVal);
System.out.println("----afterReturning()----");
}

// 环绕增强对应方法
public void aroundMethod(ProceedingJoinPoint pjp) {
System.out.println("----aroundMethod()----");
System.out.println("args[0]:" + pjp.getArgs()[0]);
System.out.println("----aroundMethod()----");
}

// 抛出异常增强
public void afterThrowingMethod(IllegalArgumentException iae) {
System.out.println("----afterThrowingMethod()----");
System.out.println("exception msg:" + iae.getMessage());
System.out.println("----afterThrowingMethod()----");
}

// final增强
public void afterMethod() {
System.out.println("----afterMethod()----");
}

// ------------绑定连接点参数----------//
public void bindParams(int num, String name) {
System.out.println("----bindParams()----");
System.out.println("name:" + name);
System.out.println("num:" + num);
System.out.println("----bindParams()----");
}
}


<?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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean id="adviceMethods" class="com.team.gaoguangjin.springinaction.schemaAspectAop.AdviceMethods" />
<aop:config proxy-target-class="true">
<!--可以配置多个 aop:aspect -->
<aop:aspect ref="adviceMethods">
<!-- args(name) 这个name属性为 public void preGreeting(String name)-->
<aop:pointcut expression="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp) and args(name)" id="playGamePointcut"/>
<!--1PlayGameImp类的所有方法运行之前 都会触发这个条件
<aop:before method="preGreeting" pointcut="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp) and args(name)" arg-names="name" />
-->
<!--2PlayGameImp类的所有方法运行之前 都会触发这个条件 1和2一样,只是用到了pointcut-->
<aop:before method="preGreeting" pointcut-ref="playGamePointcut"/>

<!--某个类的任何方法运行之后都会带返回参数的aop  -->
<aop:after-returning method="afterReturning"
pointcut="target(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp)"
returning="retVal" />
<aop:around method="aroundMethod" pointcut="execution(* playLOL(..)) and within(com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameImp)"  />
</aop:aspect>
</aop:config>

<!-- 基于schemaAspectAop的配置切面-->
<bean id="playGameAfterAdvice" class="com.team.gaoguangjin.springinaction.schemaAspectAop.PlayGameAfterAdvice" />
<aop:config proxy-target-class="true">
<aop:advisor advice-ref="playGameAfterAdvice"  pointcut="execution(* com..*.playLOL(..))"/>
</aop:config>
</bean>


测试就和普通测试一样略。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: