简介

当积累的知识点到一定量的时候,学新知识就变得容易多了。希望再接下来的学习顺利进行下去。今天知识也是挺简单的,主要就是AOP面向切面编程。其中牵涉到了JDKProxy和CGLIB两个代理类,如何使用好,加以深刻理解。学起Spring切面编程也就简单多了

代理模式

1. 代理模式介绍
	代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
2. 	代理分了三个角色
	抽象主题角色:就是代理类和被代理的类共同的接口
	代理主题角色:就是代理类中持有了被代理类的引用,代理类通常在将客户端调用传递给现在创建的对象之前或之后,都要执行某个操作,而不是单纯地将调用传递给现有的对象
	真实主题:就是代理类产生的真正的对象
3. JDK动态代理
public static <T> T getBean(final Class<T> clazz) {
	IUserService proxyService = (IUserService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
		Object retVal = null;
		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			if("saveUser".equals(method.getName()) || "updateUser".equals(method.getName())) {
				security();
				retVal = method.invoke(clazz.newInstance(), args);	//调用目标对象
			}
			return retVal;
		}
	});
}
总结:
	1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
	2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
	3、利用JDKProxy方式必须有接口的存在。
	4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型
	
4. CGLIB代理
private Class clazz;
public <T> T getEnhancer(Class<T> clazz) {
	this.clazz = clazz; 
	Enhancer enhancer = new Enhancer();
	enhancer.setClassLoader(clazz.getClassLoader());
	enhancer.setSuperclass(clazz);

	enhancer.setCallback(this);

	return (T) enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args,
		MethodProxy proxyMethod) throws Throwable {
	Object retVal = null;
	if("saveUser".equals(method.getName()) || "updateUser".equals(method.getName())) {
		security();
		retVal = method.invoke(clazz.newInstance(), args);
		System.out.println(proxyMethod.getSignature().getName()+">>>>>>>>>");
	}
	return retVal;
}
总结: cglib代理
	1. 首先要实现MethodInterceptor,重写intercept方法
	2. 创建Enhancer对象,类加载器,加载类,回调方法setCallback,创建对象

Spring面向切面编程

1. Spring面向切面编程介绍
	Spring提供2个代理模式,一个是jdk代理,另一个cglib代理
		1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
		2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

	注意:开发时尽量使用接口的编程,
		(1)对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
		(2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。
		(3) spring只支持方法连接点,不支持属性的连接点
		
2. AOP概念
	JDKProxy代理			SpringAop
	目标对象				目标对象(Target)
	拦截器类				切面(Aspect)
	拦截器类中的方法		通知(Advice)
	被拦截到的目标类中方法的集合	切入点(Pointcut)
	在客户端调用的方法(目标类目标方法)	连接点(joinpoint)
	代理类					AOP代理(JDK&CGLIB)
	代理类的代理方法生成的过程		织入(Weaving)
	
	***通知根据拦截目标类中的目标方法的位置不一样可以分为:前置通知、后置通知、最终通知、环绕通知、异常通知

 

AOP编程实现方式

1. XML形式实现
	1). SpringAOP编程,需要引入的jar包
	2). 配置applicationContext.xml文件
		a. 引入aop的约束文件
		b. 声明切面(其实就是定义一个bean节点,class为切面类)
		c. 声明切面配置<aop:config>
		d. 定义切面,相当于给切面注入灵魂<aop:aspect id="" ref="">
		e. 声明切入点<aop:pointcut id="" expression="">	指定项目中哪个作为切入点
		f. 定义通知<aop:before pointcut-ref="" method="">	注入切入点,声明哪个方法要进行通知
2. 注解方式实现
	1). SpringAOP编程,需要引入的jar包
	2). 配置applicationContext.xml文件
		a. 引入aop的约束文件
		b. 声明切面对象/声明接口实现类对象<bean>
		c. /**在类上面加入切面*/@Aspect		//表示在spring容器中定义:<aop:aspect id="aa" ref="security">
		d. 声明切入点
			@Pointcut(value="execution(* cn.itcast.f_aspectJ.a_before.UserServiceImpl.saveUser(..))")
			public void save(){};
		e. 加入通知@AfterReturning(value="save() || find()",returning="returnValue")
3. 通知详解
	1). 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (
	 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)
	2). 前置通知(before):在访问目标对象方法之前,先执行通知定义的方法
    	特点:如果代理对象(切面)中的方法(通知)抛出异常,此时不会执行目标对象
	3). 后置通知(after-returning):在访问目标对象方法之后,再执行通知定义的方法
		特点:1:如果在目标对象中抛出异常,此时不会执行通知
			  2:因为是先执行目标对象中的方法,再执行通知,所以能不能在通知中获取目标对象的方法的返回值?能
				第一步:在spring容器中定义:returning="returnValue"
				第二步:在通知的方法中的第二个参数,可以指定Object类型
	4). 异常通知:在访问目标对象方法之后,前提是目标对象方法中抛出异常,此时才会执行通知定义的方法
	特点:1:只有目标对象方法中抛出异常,此时才会执行通知
		  2:在通知的方法中捕获异常
			第一步:在spring容器中定义
			第二步:在通知的方法中的第二个参数的位置,可以指定,例如public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){
				* 要求一:获取目标对象抛出的异常的参数要放置在第二个参数的位置
				* 要求二:类型必须指定Throwable类型
				* 要求三:Throwable对应的属性值要和spring容器中定义的throwing="throwingValue"值要相匹配
	5). 最终通知:在访问目标对象方法之后,不管是否抛出异常,此时都会执行通知定义的方法
		特点:1:不管目标对象是否抛出异常,都会执行通知的方法			
	6). 环绕通知:
		a. 最后一种通知是环绕通知。环绕通知在一个目标对象方法执行之前和之后执行。它使得通知有机会
		b. 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
		环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。 
		请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)

切入点表达式(Expression)

1). 其实execution中方法全名,5个参数:带?号表示非必填项
	execution( modifiers-pattern? 
	ret-type-pattern 
	declaring-type-pattern? 
	name-pattern(param-pattern)
	throws-pattern?
	)
2). 例子:
	任意公共方法的执行:
	execution(public * *(..))

	任何一个名字以“set”开始的方法的执行:
	execution(* set*(..))

	AccountService接口定义的任意方法的执行:
	execution(* com.xyz.service.AccountService.*(..))

	在service包中定义的任意方法的执行:
	execution(* com.xyz.service.*.*(..))

	重点:
	在service包或其子包中定义的任意方法的执行:
	execution(* com.xyz.service..*.*(..))
总结:
	* 代表所有,可以理解成通配符
	*(..) 代表所有方法
	save*(*,String) 代表以save开头的方法,第一个参数为任意类型,第二个参数为string类型

 

Spring+JDBC

Jdbc编程特点
	静态代码+动态变量 = jdbc编程。在spring中动态变量可以用注入的形式给予。这样的编程方式适合包装成模板。静态代码构成了模板,而动态变量则是需要传入的参数

JDBCTemplate编码
1. 导包在spring中依赖包和核心包中找
	com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
	com.springsource.org.apache.commons.pool-1.5.3.jar
	com.springsource.org.junit-4.7.0.jar
	mysql-connector-java-5.0.8-bin.jar
	spring-jdbc-3.2.0.RELEASE.jar
	spring-tx-3.2.0.RELEASE.jar
2. 在applicationContext.xml中配置DBCP连接池
	<!-- 配置DBCP连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<property name="url" value="jdbc:mysql://localhost:3306/testspring?useUnicode=true&amp;characterEncoding=utf8"></property>
	<property name="username" value="root"></property>
	<property name="password" value="root"></property>
	
	<!-- 连接池启动时的初始值 -->
	<property name="initialSize" value="1"/>
	<!-- 连接池的最大值 -->
	<property name="maxActive" value="500"/>
	<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
	<property name="maxIdle" value="2"/>
	<!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->
	<property name="minIdle" value="1"/>
3. 	注入accountDao属性
	<!-- 创建Dao的对象 -->
    <bean id="accountDao" class="cn.itcast.b_crud.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
	<!-- 创建spring提供的Jdbc模板,用来操作数据库 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    	    <property name="dataSource" ref="dataSource"></property>
    </bean>
4. 在DAO中操作数据库
	private JdbcTemplate jdbcTemplate;
	CUD使用udpate方法,查询使用query方法
	
#Spring整合junit,完成测试
	1. 导入•org.springframework.test-3.0.2.RELEASE.jar
	2. 加入注解,@RunWith(value=SpringJUnit4ClassRunner.class)
	@ContextConfiguration(value="classpath:cn/itcast/b_crud/beans.xml")
	3. @Resource(name="accountDao")