在所编写本章时,我所在的公司正在大量裁人,我所在的项目组,也正处于闲置阶段。我们说每天上都在上下班。这才是正确的工作流程。在我们上下班的时候,有许多与工作无关,但是你又必须得去做的事情,比如我从事java web开发,但是我每天上下班都得打卡,每天都得做绩效。每天都有可能被开除等等。
那么我们首先创建一个Person类。里面有我们的员工编号,名字和所属部门,还有考核分数;
public class Person implements Serializable{
private String id; //工作编号
private String name; //名字
private String department; //所属部门
private Integer score; //考核分数
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
}
接着我们模拟一下每天上班的流程:
public interface Work {
//上班
public void goTOWork(Person person);
//工作开发
public void workExploit(Person person);
//下班
public void closedWork(Person person);
}
public class WorkJob implements Work{
/**
* @param person
* @see org.seckill.entity.Work#goTOWork(org.seckill.entity.Person)
*<pre>
*<li>Author: </li>
*<li>Date: 2016年9月26日</li>
*</pre>
*/
@Override
public void goTOWork(Person person) {
System.out.println(person.getName()+"开始上班喽");
}
/**
* @param person
* @see org.seckill.entity.Work#workExploit(org.seckill.entity.Person)
*<pre>
*<li>Author: </li>
*<li>Date: 2016年9月26日</li>
*</pre>
*/
@Override
public void workExploit(Person person) {
System.out.println(person.getName()+"开始工作啦");
}
/**
* @param person
* @see org.seckill.entity.Work#closedWork(org.seckill.entity.Person)
*<pre>
*<li>Author: </li>
*<li>Date: 2016年9月26日</li>
*</pre>
*/
@Override
public void closedWork(Person person) {
System.out.println("结束工作下班");
}
}
最后写个测试方法,测试下。
public static void main(String[] args) {
Person person=new Person();
person.setId("00054");
person.setName("张三");
person.setDepartment("研发部");
person.setScore(50);
WorkJob workJob=new WorkJob();
workJob.goTOWork(person);
workJob.workExploit(person);
workJob.closedWork(person);
}
张三开始上班喽张三开始工作啦
张三结束工作下班
如果说现在公司要求上下班打卡怎么办,我们是不是要写个打卡的方法。按照面向对象编程来说的话,我们不会主动参与调用,方法的发生,在软件开发中,分布于应用中多处的功能被称为横切关注点。通常,这些横切关注点与业务逻辑是分离的。将这些关注点分离真是面向切面编程(AOP)所有解决的。
委托和继承:
在AOP没有出现之前用的最多的就是委托和继承去实现了。
假设我们创建工作的辅助类。
public class WorkGrade {
public void openCard(Person person){
System.out.println(person.getName()+"开始打卡");
}
}
然后让我们的工作类再去继承一下。
public class WorkJob extends WorkGradeimplements Work
最后再去测试一下。
public static void main(String[] args) {
Person person=new Person();
person.setId("00054");
person.setName("张三");
person.setDepartment("研发部");
person.setScore(50);
WorkJob workJob=new WorkJob();
workJob.goTOWork(person);
workJob.workExploit(person);
workJob.closedWork(person);
workJob.openCard(person);
}
张三开始上班喽
张三开始工作啦
张三结束工作下班
张三开始打卡
委托模式这里就不写了,有兴趣的可以自行了解一下。
定义AOP术语:
1.通知(Advice):
通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
Before-在方法被调用之前调用。
After-在方法完成之后调用,无论方法执行是否成功。
After-returning-在方法成功执行之后调用通知。
After-throwing-在方法抛出异常后调用通知。
Around-通知包裹了被通知的方法,在被通知的方法调用之前和调用方法之后执行自定义的行为。
2.连接点(Joinpoint):
程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3.切入点(Pointcut)
通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们方便的用正则表达式来指定
4.切面(Aspect)
通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5.引入(Introduction)
引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6.目标(Target)
即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)
7.代理(proxy)
应用通知的对象,详细内容参见设计模式里面的代理模式
8.织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
(1)编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
(2)类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
(3)运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
Spring提供了4种实现AOP的方式:
1.经典的基于代理的AOP2.@AspectJ注解驱动的切面
3.纯POJO切面
4.注入式AspectJ切面(适合各个版本)
前三种方法都是spring基于代理的AOP变体,因此,Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单方法拦截的范畴(比如构造器或熟悉拦截)。那么应该考虑在Aspectj里面实现切面。利用spring的DI把springBean注入到Aspectj切面中。
一、基于代理的AOP
首先创建一个增强类:
public class WorkGrade implements MethodBeforeAdvice, AfterReturningAdvice{
/**
* @param returnValue
* @param method
* @param args
* @param target
* @throws Throwable
* @see org.springframework.aop.AfterReturningAdvice#afterReturning(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
*<pre>
*<li>Author: </li>
*<li>Date: 2016年9月26日</li>
*</pre>
*/
//方法执行成功后
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
Person person=(Person) args[0];
System.out.println(person.getName()+"结束打卡");
}
/**
* @param method
* @param args
* @param target
* @throws Throwable
* @see org.springframework.aop.MethodBeforeAdvice#before(java.lang.reflect.Method, java.lang.Object[], java.lang.Object)
*<pre>
*<li>Author: </li>
*<li>Date: 2016年9月26日</li>
*</pre>
*/
//方法执行前
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
Person person=(Person) args[0];
System.out.println(person.getName()+"开始打卡");
}
}
再创建一下自己的XML文件;
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 创建一个增强 advice -->
<bean id ="workHelper" class="org.seckill.entity.WorkGrade"/>
<bean id="lina" class="org.seckill.entity.WorkJob"/>
<!-- 定义切点 匹配所有的上下班方法-->
<bean id ="workPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*Work"></property>
</bean>
<!-- 切面 增强+切点结合 -->
<bean id="workHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="workHelper"/>
<property name="pointcut" ref="workPointcut"/>
</bean>
<!-- 定义代理对象 -->
<bean id="linaProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="lina"/>
<property name="interceptorNames" value="workHelperAdvisor"/>
</bean>
</beans>
测试类:
@Test
public void test(){
ApplicationContext ct = new ClassPathXmlApplicationContext("spring/spring-aop.xml");
Person person=new Person();
person.setId("00054");
person.setName("张三");
person.setDepartment("研发部");
person.setScore(50);
Work workJob =(Work) ct.getBean("linaProxy");
workJob.goTOWork(person);
workJob.workExploit(person);
workJob.closedWork(person);
}
张三开始打卡张三开始上班喽
张三结束打卡
张三开始工作啦
张三开始打卡
张三结束工作下班
张三结束打卡
pattern属性指定了正则表达式,他匹配所有的上下班方法
使用org.springframework.aop.support.DefaultPointcutAdvisor的目的是为了使切点和增强结合起来形成一个完整的切面
最后配置完后通过org.springframework.aop.framework.ProxyFactoryBean产生一个最终的代理对象。
二、纯简单java对象切面
纯简单java对象切面在我看来就是相对于第一种配置,不需要使用代理,,而是通过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:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 创建一个增强 advice -->
<bean id ="workHelper" class="org.seckill.entity.WorkGrade"/>
<bean id="lina" class="org.seckill.entity.WorkJob"/>
<!-- 配置切点和通知-->
<bean id ="sleepAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="workHelper"></property>
<property name="pattern" value=".*Work"/>
</bean>
<!-- 自动代理配置 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
</beans>
测试:
@Test
public void test(){
ApplicationContext ct = new ClassPathXmlApplicationContext("spring/spring-aop2.xml");
Person person=new Person();
person.setId("00054");
person.setName("张三");
person.setDepartment("研发部");
person.setScore(50);
Work workJob =(Work) ct.getBean("lina");
workJob.goTOWork(person);
workJob.workExploit(person);
workJob.closedWork(person);
}
这种代理模式也被称为自动代理,因为DefaultAdvisorAutoProxyCreator这个类功能更为强大,这个类的奇妙之处是他实现了BeanProcessor接口,当ApplicationContext读如所有的Bean配置信息后,这个类将扫描上下文,寻找所有的Advistor(一个Advisor是一个切入点和一个通知的组成),将这些Advisor应用到所有符合切入点的Bean中。
三、@Aspect注解形式
@Aspect
@Component
public class WorkGrade2{
@Pointcut("execution(* *.Work(..))")
public void sleeppoint(){}
@Before("workpoint()")
public void beforeSleep(){
System.out.println("开始打卡");
}
@AfterReturning("workpoint()")
public void afterSleep(){
System.out.println("结束打卡");
}
}
<!--扫描包 -->
<context:component-scan base-package="org.seckill.aop"
annotation-config="true" />
<!-- ASPECTJ注解 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 目标类 -->
<bean id="workJob" class="org.seckill.entity.WorkJob" />
四、注入形式的Aspcet切面
这个是最简单,最灵活,也是最常用的一个。
public class WorkGrade2{
public void beforeWork(){
System.out.println("开始打卡");
}
public void afterWork(){
System.out.println("结束打卡");
}
}
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目标类 -->
<bean id="lina" class="org.seckill.entity.WorkJob"/>
<bean id ="workHelper" class="org.seckill.aop.WorkGrade2"/>
<aop:config>
<aop:aspect ref="workHelper">
<aop:before method="beforeWork" pointcut="execution(* org.seckill..*Work(..))"/>
<aop:after method="afterWork" pointcut="execution(* org.seckill..*Work(..))"/>
</aop:aspect>
</aop:config>
</beans>