AOP 面向切面编程,扩展功能不修改源代码实现(配置文件)
采用横向抽取机制,取代了传统的纵向继承体系,重复性代码(性能监视,事务管理)
横向抽取机制 底层使用横向代理方式实现的
/*使用jdk动态代理,针对有接口的情况*/
public interface Dao {
public void add();
}
public class DaoImpl implements Dao {
public void add(){
//业务逻辑
}
}
//目的 增强实现类add方法
//使用动态代理方式创建 接口实现类代理对象(创建和DaoImpl平级的对象,这个对象不是真正对象,而是代理对象,它可以实现和DaoImpl相同的功能)
/*没有接口的情况*/
public class User {
public void add(){}
}
//动态代理实现增强add方法
//创建User类的子类的代理对象 在子类里面调用父类的方法(super完成)完成增强
代理类eg:
public class sUser extends User {
//使用cglib动态代理,实现没有接口
//添加逻辑
super.add();
}
操作术语:
public class Demo{
public void add(){}
public void update(){}
public void delete(){}
public void find(){}
}
Joinpoint连接点: 指可以被增强的方法(被称为连接点),spring中只支持方法类型的连接点
pointcut切入点: 在类中有很多方法可以被增强,实际操作中只增强了类中的某几个方法,则这些被实际增强的方法称为切入点
Advice通知/增强: 被拦截到的连接点Joinpoint之后要做的事情,增强的逻辑称为增强,比如扩展日志功能,这个日志功能称为增强。分为前置通知(方法前)、后置通知(方法后)、异常通知(方法出现异常)、最终通知(后置通知之后)、环绕通知(方法之前和之后)
**切面:**把增强应用到具体方法上这个过程称为切面
spring中AOP操作
在spring中进行aop操作,使用aspectj实现,aspectj不是spring的一部分,它和spring一起进行aop操作,spring2.0后增加了Aspectj支持
使用aspectj实现aop有两种方式
①基于aspectj的xml方式
使用表达式配置切入点
常用表达式
execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
*代表所有修饰符 空格 增强方法的全路径(包含他的参数)
(1)execution(* com.idea.aop.User.add(…))
(2)execution(* com.idea.aop.User.*(…)) //*代表类中所有方法
(3)execution(* *.*(…)) //所有类中的所有方法 都被增强
(4)execution(* save*(…)) //匹配所有save开头的方法
<!--struts.xml配置文件-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!-- 所有匹配*.action的请求都由struts2处理 -->
<constant name="struts.action.extension" value="action" />
<!-- 是否启用开发模式 -->
<constant name="struts.devMode" value="true" />
<!-- struts配置文件改动后,是否重新加载 -->
<constant name="struts.configuration.xml.reload" value="true" />
<!-- 设置浏览器是否缓存静态内容 -->
<constant name="struts.serve.static.browserCache" value="false" />
<!-- 请求参数的编码方式 -->
<constant name="struts.i18n.encoding" value="utf-8" />
<!-- 每次HTTP请求系统都重新加载资源文件,有助于开发 -->
<constant name="struts.i18n.reload" value="true" />
<!-- 文件上传最大值 -->
<constant name="struts.multipart.maxSize" value="104857600" />
<!-- 让struts2支持动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
<!-- Action名称中是否还是用斜线 -->
<constant name="struts.enable.SlashesInActionNames" value="false" />
<!-- 允许标签中使用表达式语法 -->
<constant name="struts.tag.altSyntax" value="true" />
<!-- 对于WebLogic,Orion,OC4J此属性应该设置成true -->
<constant name="struts.dispatcher.parametersWorkaround" value="false" />
<package name="basePackage" extends="struts-default">
</package>
</struts>
web.xml配置过滤器
<!--2.1.3以前:
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
期间:
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterr</filter-class>
2.5以后:
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
-->
<!-- 过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterr</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
/**
* action
*/
public class UserAction extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("action");
ApplicationContext context =
new ClassPathXmlApplicationContext("applictionContext.xml");
UserService userService = (UserService)context.getBean("userService");
userService.add();
return NONE;
}
}
问题:每次访问action都要加载一次配置文件,功能没问题,但是影响性能,现在将加载配置文件让项目启动时完成(使用监听器、ServletContext对象,在spring(spring-web jar包)中已经封装好监听器,只需配置即可)
<!--web.xml-->
<!-- 指定spring配置文件路径 -->
<context-param>
<!--名字固定
ContextLoaderListener类的父类ContextLoader中
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
-->
<param-name>contextConfigLocation</param-name>
<!--classpath即src根目录
配置spring配置文件的全路径
-->
<!--2、部署applicationContext的xml文件-->
<!--如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,
在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。
如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。
也可以这样applicationContext-*.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,
applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。
在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。-->
<param-value>classpath:applictionContext.xml</param-value>
</context-param>
<!-- 配置监听器 路径不用改-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
②基于aspectj的注解方式
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行