一.动态代理
1.1 动态代理的原理
代理设计模式的原理:使用一个代理将对象封装起来,然后用代理对象取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及一何种方式将方法调用转到原始对象上。
1.2动态代理的方式
1.基于接口实现动态代理:JDK动态代理
2.基于继承实现动态代理:Cglib、javassist动态代理
1.JDK的动态代理
(1) Proxy
它是所有动态代理类的父类,专门用来生成代理类或者是代理对象。
//用来生成代理类的Class对象
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
//用来生成代理对象
/**
loader:ClassLoader对象,类加载器对象,帮我们加载动态生成的代理类;
interfaces:接口们,提供目标对象的所有接口。
h:InvocationHandler 对象
**/
ClassLoader loader = target.getClass().getClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
public static Object newProxyInstance(ClassLoader loader,Class<?>... interfaces,InvocationHandler h)
(2) InvocationHandler
由它完成动态代理的整个过程
//invoke :代理对象调用代理方法,会回来调用invoke方法
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
//在该方法中包含Object result=method.invoke(target,args);该语句是目标对象执行目标方法
动态代理需要明白:目标对象,如何获取代理对象以及代理要做什么。实际上,动态代理类是在运行时动态生成的,因此如果需要查看该动态代理类,需要将内存中的东西抓出来,只需在测试方法中加入如下代码来保存动态生成的代理类:
//将动态生成的代理类保存下来
Properties properties = System.getProperties();
properties.put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
动态代理类中最重要的核心代码就是在某方法中执行如下语句:
super.h.invoke(this,m1,...);
二.AOP概述
2.1 AOP
AOP编程操作的主要对象是切面,而切面模块化横切关注点,横切关注点我们可以理解为功能,将这些功能抽取出来单独作为一个个的功能。在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称为切面。比如验证功能、前置日志功能、后置日志功能等可以抽取出来成为验证切面、日志切面。
AOP底层实际上就是动态代理。
1.切面:切面就是封装横切关注点信息的类,每个关注点体现为一个通知方法。
2.通知:通知就是切面必须完成的每个具体的工作,目标即就是被通知的对象。
3.代理:代理则是向目标对象应用通知之后创建的代理对象。
4.连接点:连接点则是横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置,例如:类某个方法调用前、调用后、方法捕获到异常后等。
5.切入点:切入点则是定位连接点的方式,每个类的方法中包含多个连接点,所以连接点是类中客观存在的事物,如果把连接点看作数据库中的记录,那么切入点就是查询条件,AOP可以通过切入点定位到特定的连接点。
二、AspectJ
AspectJ并不是spring本身的AOP框架,它是java社区里最完整最流行的AOP框架,在spring2.0以上版本中,可以使用AspectJ注解或者基于xml配置的AOP。AspectJ既支持JDK的代理又支持Cglib的代理。
2.1 AspectJ引入
<!--组件扫描-->
<context:component-scan base-package="com.atguigu.spring.aspectJ.annotation"></context:component-scan>
<!--基于注解使用AspectJ:主要作用就是为切面中通知能作用到的目标类生成代理-->
<aop:aspectj-autoproxy/>
在AspectJ注解中,切面只是一个带有@Aspect注解的java类,它往往包含很多通知。通知时标注了某种注解的简单java方法。AspectJ支持5中类型的通知注解。通知注解的使用一般需要在方法上包含切入点表达式,比如@Before(value=“execution(public int com.xxx.xxImpl.方法名(参数列表类型))”)
。
1.@Before:前置通知,在方法之前执行;
2.@After:后置通知,在方法执行之后执行;
3.@AfterRunning:返回通知,在方法返回结果之后执行;
4.@AfterThrowing:异常通知,在方法抛出异常之后执行;
5.@Around:环绕通知,围绕着方法执行。
如果希望通知注解作用到任意方法上,可以使用:
@Before("execution(* com.xxx.*.*(..))")
其中,第一个*
:表示任意修饰符,任意返回值;第二个*
:表示任意类;第三个*
:任意方法;..
:表示任意参数列表。
2.2 返回通知
通过参数列表中定义接收方法返回值的形参,即获取方法的返回值是通过returning 来指定一个名字,必须要与方法的一个形参名一致:
@AfterReturning(value="execution(* com.xxx.*.*(..)),returning=“result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
//方法的名字
String methodName=joinPoint.getSignature().getName();
//其他代码
}
2.3 异常通知
异常通知是在目标方法抛出异常后执行。获取异常信息时在注解中使用throwing=”变量名“
来获取异常信息。如下:
@AfterThrowing(value="execution(* com.xxx.*.*(..)),throwing=“ex")
public void afterReturningMethod(JoinPoint joinPoint, Exception ex){
//方法的名字
String methodName=joinPoint.getSignature().getName();
//其他代码
}
获取方法的异常时,通过throwing来指定一个名字,必须要与方法的一个形参名一致。可以通过形参中异常的类型来设置抛出指定异常才会执行异常通知。如果想要在方法抛出空指针异常时才执行的话,那么就需要:
@AfterThrowing(value="execution(* com.xxx.*.*(..)),throwing=“ex")
public void afterThrowingMethod(JoinPoint joinPoint,NullPoint Exception ex){
//方法的名字
String methodName=joinPoint.getSignature().getName();
//其他代码
}
2.4 环绕通知
环绕通知可以理解为环绕这目标方法执行,可以理解为前置、后置、返回以及异常通知的结合体,更像是动态代理的整个过程。
@Around(value="execution(* com.xxx.*.*(..)))
public Object aroundMethod(ProceedingJoinPoint pjp){
//执行目标方法
try{
//前置通知
Object result = pjp.proceed();
//返回通知
return result;
}catch(Throwable e){
//异常通知
e.printStackTrace();
}finally{
//后置通知
}
return null;
}
2.5 切面的优先级
当有多个切面作用于同一目标对象时,这时往往需要设置各切面的优先级,此时可以在切面类上通过Order(数值)
注解来设置切面的优先级,默认为2147483647,为最大值,却是最低的优先级,值越小,其优先级越高,因此当设置值为1时,优先级最高。
2.6 重用切入点表达式
当切面中的通知多次用到同一个切入点表达式时,可以将切入点表达式声明为一个方法:
public class LoggingAspect{
//声明切入点表达式
@PointCut("execution(* com.xxx.*.*(..)))
public void declarePointCut(){}
//同一类中重用切入点表达式
@After(“declarePointCut()”)
public void afterMethod(JoinPoint joinPoint){
//方法的名字
String methodName=joinPoint.getSignature().getName();
//其他代码
}
}
不同切面类重用切入点表达式:
public class ValidationAspect{
//不同类中重用切入点表达式
@After(“ LoggingAspect.declarePointCut()”)
public void afterMethod(JoinPoint joinPoint){
//方法的名字
String methodName=joinPoint.getSignature().getName();
//其他代码
}
}
三、以XML方式配置切面
<!--目标对象-->
<bean id="arithmticCalculatorImpl" class="com.atguigu.spring.aspectJ.xml.ArithmeticCalculatorImpl"></bean>
<!--切面-->
<bean id="loggingAspect" class="com.atguigu.spring.aspectJ.xml.LoggingAspect"></bean>
<bean id="validationAspect" class="com.atguigu.spring.aspectJ.xml.ValidationAspect"></bean>
<!--aop:切面 通知 切入点表达式-->
<aop:config>
<!--切面1-->
<aop:aspect ref="loggingAspect" order=“2”>
<!--切入点表达式-->
<aop:pointcut expression="execution(* com.atguigu.spring.aspectJ.xml.*.*(..))" id="myPointCut"/>
<!--通知-->
<aop:before method="beforeMethod" pointcut-ref="myPointCut"/>
<!--如果切入点表达式不一样,可以采用如下pointcut重新指定切入点表达式-->
<aop:after method="afterMethod" pointcut="execution(* com.atguigu.spring.aspectJ.xml.*.*(..))" pointcut-ref="myPointCut"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="myPointCut" returning="res"/>
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="myPointCut" throwing="ex"/>
<aop:around method="aroundMethod" pointcut-ref="myPointCut"/>
</aop:aspect>
<!--切面2-->
<aop:aspect ref="validationAspect" order=“1”>
<!--切入点表达式-->
<aop:pointcut expression="execution(* com.atguigu.spring.aspectJ.xml.*.*(..))" id="myPointCut"/>
<!--通知-->
<aop:before method="beforeMethod" pointcut-ref="myPointCut"/>
<!--如果切入点表达式不一样,可以采用如下pointcut重新指定切入点表达式-->
<aop:after method="afterMethod" pointcut="execution(* com.atguigu.spring.aspectJ.xml.*.*(..))" pointcut-ref="myPointCut"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="myPointCut" returning="res"/>
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="myPointCut" throwing="ex"/>
<aop:around method="aroundMethod" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
四、JdbcTemplate
4.1 概述
Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。JDBC模板的设计目的时为了不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化框架。
4.2 xml配置
<!---数据源->
<context:property-placeholder location="classpath:db,properties"/>
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
4.3 持久化操作
1.update():增删改操作
(1)定义sql语句的字符串
String sql = “insert into stu(id,name,sex) value(?,?,?)”;
(2)调用方法
dbcTemplate.update(sql,sql语句参数列表);
2.batchUpdate(String List<Object[]>):批量增删改
3.queryForObject(String, RowMapper,Object…):查询单行
4.query(String, RowMapper,Object ):查询多行
5.queryForObject(String,Class,Object…):查询单一值