鲁春利的工作笔记,好记性不如烂笔头
面向方面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。
package com.invicme.apps.aop.proxy;
import org.apache.log4j.Logger;
/**
*
* @author lucl
*
* 数学计算实现类
*
*/
public class ArithmeticCalculateImpl implements ArithmeticCalculate {
private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);
private int i = 0;
private int j = 0;
public ArithmeticCalculateImpl () {
this(0, 0);
}
public ArithmeticCalculateImpl (int i, int j) {
this.i = i;
this.j = j;
}
@Override
public int add(int i, int j) {
logger.info("The method add was invoke with args [" + i + ", " + j + "]");
int sum = i + j;
logger.info("The method add ends with result [" + sum + "]");
return sum;
}
@Override
public int div(int i, int j) throws DivisorIsZeroException {
logger.info("The method div was invoke with args [" + i + ", " + j + "]");
if (j == 0) {
throw new DivisorIsZeroException("除数不可为0");
}
int result = i / j;
logger.info("The method div ends with result [" + result + "]");
return result;
}
@Override
public String validateNum(String level, int i) {
logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");
String result = this.getMsg(i);
logger.info("The method validateNum ends with result [" + result + "]");
return result;
}
private String getMsg (int i) {
if (i > 0) {
return "正数";
}
return "负数";
}
}
AOP为开发者提供一种进行横切关注点分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦合的完成了我们的功能。
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上Joinpoint还可以是Field或类构造器。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知):所谓通知是指拦截到Joinpoint之后所要做的事情。通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象):代理的目标对象。
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入。
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。
Spring提供了4种实现AOP的方式:
经典的基于代理的AOP
@AspectJ注解驱动的切面
纯POJO切面
注入式AspectJ切面
前三种都是Spring基于代理的AOP变体,因此Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单方法的拦截范畴(如构造器或属性拦截),那么应该考虑在AspectJ里实现切面,利用Spring的DI把Spring Bean注入到AspectJ切面中。
经典的基于代理的AOP:
Spring支持五种类型的通知:
Before(前) org.apringframework.aop.MethodBeforeAdvice
After-returning(返回后) org.springframework.aop.AfterReturningAdvice
After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
Arround(周围) org.aopaliance.intercept.MethodInterceptor
Introduction(引入) org.springframework.aop.IntroductionInterceptor
实现步骤:
1.创建通知:实现这几个接口,把其中的方法实现了
2.定义切点和通知者:在Spring配制文件中配置这些信息
3.使用ProxyFactoryBean来生成代理。
业务代码(接口):
package com.invicme.apps.aop.proxy;
/**
*
* @author lucl
*
* 数学计算接口类
*
*/
public interface ArithmeticCalculate {
public int add (int i, int j);
public int div (int i, int j) throws DivisorIsZeroException;
public String validateNum (String level, int i);
}
业务代码(实现类):
package com.invicme.apps.aop.proxy;
import org.apache.log4j.Logger;
/**
*
* @author lucl
*
* 数学计算实现类
*
*/
public class ArithmeticCalculateImpl implements ArithmeticCalculate {
private static final Logger logger = Logger.getLogger(ArithmeticCalculateImpl.class);
private int i = 0;
private int j = 0;
public ArithmeticCalculateImpl () {
this(0, 0);
}
public ArithmeticCalculateImpl (int i, int j) {
this.i = i;
this.j = j;
}
@Override
public int add(int i, int j) {
logger.info("The method add was invoke with args [" + i + ", " + j + "]");
int sum = i + j;
logger.info("The method add ends with result [" + sum + "]");
return sum;
}
@Override
public int div(int i, int j) throws DivisorIsZeroException {
logger.info("The method div was invoke with args [" + i + ", " + j + "]");
if (j == 0) {
throw new DivisorIsZeroException("除数不可为0");
}
int result = i / j;
logger.info("The method div ends with result [" + result + "]");
return result;
}
@Override
public String validateNum(String level, int i) {
logger.info("The method validateNum was invoke with args [" + level + ", " + i + "]");
String result = this.getMsg(i);
logger.info("The method validateNum ends with result [" + result + "]");
return result;
}
private String getMsg (int i) {
if (i > 0) {
return "正数";
}
return "负数";
}
}
希望切入的日志操作:
package com.invicme.apps.aop.proxy;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
/**
*
* @author lucl
*
*/
public class LogAdapter implements MethodBeforeAdvice, AfterReturningAdvice {
private static final Logger logger = Logger.getLogger(LogAdapter.class);
/**
* 目标对象的方法执行之前打印日志
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
logger.info(target.getClass().getName() + "@" + method.getName() + " with args " + Arrays.asList(args) + " invoke.");
}
/**
* 目标对象的方法执行之后打印日志
*/
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
logger.info(target.getClass().getName() + "@" + method.getName() + " ends with result " + returnValue + " .");
}
}
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:context="http://www.springframework.org/schema/context"
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">
<!-- 业务类 -->
<bean id="calculate" class="com.invicme.apps.aop.proxy.ArithmeticCalculateImpl" />
<!-- 切入的日志类 -->
<bean id="logAdapter" class="com.invicme.apps.aop.proxy.LogAdapter" />
<!-- 定义正则表达式切点 -->
<bean id="loggerPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<!-- 单个方法 -->
<!--
<property name="pattern" value=".*add"/>
-->
<property name="patterns">
<list>
<value>.*add</value>
<value>.*iv</value>
<value>.validate*</value>
</list>
</property>
</bean>
<bean id="loggerAdapterAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAdapter"/>
<property name="pointcut" ref="loggerPointcut"/>
</bean>
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="calculate"/>
<property name="interceptorNames" value="loggerAdapterAdvisor" />
<property name="proxyInterfaces" value="com.invicme.apps.aop.proxy.ArithmeticCalculate" />
</bean>
</beans>
Junit单元测试类:
package com.test.apps.spring.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.invicme.apps.aop.proxy.ArithmeticCalculate;
import com.invicme.apps.aop.proxy.DivisorIsZeroException;
/**
*
* @author lucl
*
*/
public class TestSpringProxyFactoryBean {
@Test
public void testProxyFactoryBean() {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop.xml");
ArithmeticCalculate calculate = context.getBean("proxyFactoryBean", ArithmeticCalculate.class);
calculate.add(1, 2);
System.out.println("----------------------------------------------------------------");
try {
calculate.div(12, 3);
} catch (DivisorIsZeroException e) {
e.printStackTrace();
}
}
}
输出结果:
说明:
ProxyFactoryBean是一个代理,我们可以把它转换为proxyInterfaces中指定的实现该interface的代理对象。
通过代理模式可以获取到业务日志中希望输出的日志内容。
OK!这是我们想要的结果,但是上面这个过程貌似有点复杂,尤其是配置切点跟通知,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:context="http://www.springframework.org/schema/context"
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">
<!-- 业务类 -->
<bean id="calculate" class="com.invicme.apps.aop.proxy.ArithmeticCalculateImpl" />
<!-- 切入的日志类 -->
<bean id="logAdapter" class="com.invicme.apps.aop.proxy.LogAdapter" />
<!-- 定义正则表达式切点 -->
<bean id="loggerPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<!-- 单个方法 -->
<!--
<property name="pattern" value=".*add"/>
-->
<property name="patterns">
<list>
<value>.*add</value>
<value>.*iv</value>
<value>.validate*</value>
</list>
</property>
</bean>
<bean id="loggerAdapterAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAdapter"/>
<property name="pointcut" ref="loggerPointcut"/>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<!-- <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="calculate"/>
<property name="interceptorNames" value="loggerAdapterAdvisor" />
<property name="proxyInterfaces" value="com.invicme.apps.aop.proxy.ArithmeticCalculate" />
</bean> -->
</beans>
执行程序:
package com.test.apps.spring.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.invicme.apps.aop.proxy.ArithmeticCalculate;
import com.invicme.apps.aop.proxy.DivisorIsZeroException;
/**
*
* @author lucl
*
*/
public class TestSpringProxyFactoryBean {
@Test
public void testProxyFactoryBean() {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context-aop.xml");
/*ArithmeticCalculate calculate = context.getBean("proxyFactoryBean", ArithmeticCalculate.class);*/
ArithmeticCalculate calculate = context.getBean("calculate", ArithmeticCalculate.class);
calculate.add(1, 2);
System.out.println("----------------------------------------------------------------");
try {
calculate.div(12, 3);
} catch (DivisorIsZeroException e) {
e.printStackTrace();
}
}
}
说明:org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator能够为方法匹配的bean自动创建代理!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。