Spring的学习(三)
1.AOP实现拦截
1.1简介
AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
AOP 的好处:
每个事务逻辑位于一个位置, 代码不分散, 便于维护和升级
业务模块更简洁, 只包含核心业务代码. 非侵入式编程
在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。(类似于拦截器,在特定位置执行特定方法后继续运行)
1.2AOP术语
► 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
► 通知(Advice): 切面必须要完成的工作
► 目标(Target): 被通知的对象
► 代理(Proxy): 向目标对象应用通知之后创建的对象
► 连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。(满足条件的方法)
► 切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。(条件)
1.3开发环境
在有spring库的基础上引用aop库
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
Aop的接口包
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8_2</version>
</dependency>
Log4j
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
1.4代码
Spring.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 扫描 -->
<context:component-scan base-package="aop"></context:component-scan>
<!-- aop配置 -->
<aop:config>
<!-- 定义切点(条件),类签名匹配 ,execution(*)任意类型返回值,多个包可以用*..*,可使用and or-->
<aop:pointcut expression="execution(* aop.MyHouseOwner.*(..))" id="myPointcut"/>
<!-- 定义消息 -->
<aop:aspect ref="myMessage">
<!-- 定义前置消息 pointcut-ref引用切点-->
<aop:before method="beforeSeek" pointcut-ref="myPointcut"/>
<aop:after method="afterSeek" pointcut-ref="myPointcut"/>
<aop:after-throwing method="throwException" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
Java代码:
房东类:
package aop;
import org.springframework.stereotype.Component;
@Component
public class MyHouseOwner{
public void seekMyHouse() {
System.out.println("租房");
int i=1/0;
}
}
消息类:
package aop;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;
@Component
public class MyMessage {
public void beforeSeek(){
System.out.println("打扫卫生");
System.out.println("寻找租客");
}
public void afterSeek(){
System.out.println("收租金");
}
public void throwException(JoinPoint jp){
System.out.println("出现异常");
}
}
测试类:
package aop;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* spring实现aop 用了两种技术
* 1 java动态代理(面向接口编程)
* 2 cglib实现动态代理(需要导包)
* @author Administrator
*
*/
public class Test {
static GenericXmlApplicationContext context;
static {
// 获取容器
context = new GenericXmlApplicationContext("classpath:aop/spring.xml");
}
public static void main(String[] args) {
MyHouseOwner ho=(MyHouseOwner)context.getBean("myHouseOwner");
ho.seekMyHouse();
context.close();
}
}
1.5 加入log4j实现日志功能
Spring.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 扫描 -->
<context:component-scan base-package="log"></context:component-scan>
<!-- aop配置 -->
<aop:config>
<!-- 定义切点(条件),类签名匹配 ,execution(*)任意类型返回值,多个包可以用*..*,可使用and or-->
<aop:pointcut expression="execution(* log.*.*(..))" id="myPointcut"/>
<!-- 定义消息 -->
<aop:aspect ref="myMessage">
<!-- 定义前置消息 pointcut-ref引用切点-->
<aop:before method="beforeUser" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
Java代码:
User类:
package log;
import org.springframework.stereotype.Component;
@Component
public class User {
public void login(){
System.out.println("用户登录");
}
}
消息类:
package log;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.springframework.stereotype.Component;
@Component
public class MyMessage {
Logger logger = Logger.getLogger(MyMessage.class);
public void beforeUser(JoinPoint jp){
//获取方法名
String method=jp.getSignature().toString();
logger.debug("方法"+method+"被调用");
}
}
测试类:
package log;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* spring实现aop 用了两种技术
* 1 java动态代理(面向接口编程)
* 2 cglib实现动态代理(需要导包)
* @author Administrator
*
*/
public class Test {
static GenericXmlApplicationContext context;
static {
// 获取容器
context = new GenericXmlApplicationContext("classpath:log/spring.xml");
}
public static void main(String[] args) {
User u = (User)context.getBean("user");
u.login();
}
}
Log4j.properties:
log4j.rootLogger = debug , stdout , D
### \u8F93\u51FA\u5230\u63A7\u5236\u53F0 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n
### \u8F93\u51FA\u5230\u65E5\u5FD7\u6587\u4EF6 ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = d:/logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG ## \u8F93\u51FADEBUG\u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
2.java动态代理模式实现拦截
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
例子(租房)代码:
房东接口:
public interface IHouseOwer {
public void seekMyHouse();
}
房东类:
public class HouseOwerImpl implements IHouseOwer{
public void seekMyHouse() {
System.out.println("租房");
}
}
代理类:
/**
* 代理类实现InvocationHandler接口
*/
public class HouseProxy implements InvocationHandler {
/**
* 代理房东
*/
IHouseOwer house;
public HouseProxy(IHouseOwer house) {
this.house = house;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置通知 在调用租房方法之前,代理需要做一些卖前工作
System.out.println("找租客");
System.out.println("打扫房子");
System.out.println("收租金");
System.out.println("帮忙搬家");
//目标通知 声明要调用的方法(租房)
Object obj=null;
try {
//目标通知
obj= method.invoke(house, args);
//环绕通知(前置与后置通知中间的通知)
} catch (Exception e) {
//异常通知
e.printStackTrace();
}
//后置通知 在调用租房方法之前,代理需要做一些售后工作
System.out.println("给房主租金");
return obj;
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建被拦截的类(房东类)
IHouseOwer h = new HouseOwerImpl();
//创建拦截类(中介类)
HouseProxy hp = new HouseProxy(h);
//最终生成被拦截类
IHouseOwer house=(IHouseOwer)Proxy.newProxyInstance(Test.class.getClassLoader(), h.getClass().getInterfaces(), hp);
//调用方法(租房)
house.seekMyHouse();
}
}