Spring(2)
温故而知新!复习以前学过的框架知识,肯定会有新的收获。
Java中的代理分为静态代理和动态代理(JDK代理),和CGLIB代理。
动态代理
利用JDK中的Proxy类生成代理。因此成为JDK代理。
目标对象一定要实现接口, 代理对象不用实现接口。
/**
* 动态代理:
* 代理工厂,给多个目标对象生成代理对象!
* @author Wch
*
*/
public class ProxyFactory {
// 接收一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 返回对目标对象(target)代理后的对象(proxy)
public Object getProxyInstance() {
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标对象使用的类加载器
target.getClass().getInterfaces(), // 目标对象实现的所有接口
new InvocationHandler() { // 执行代理对象方法时候触发
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 获取当前执行的方法的方法名
String methodName = method.getName();
// 方法返回值
Object result = null;
// 判断
if ("find".equals(methodName)) {
// 直接调用目标对象方法
result = method.invoke(target, args);
} else {
System.out.println("开启事务...");
// 执行目标对象方法
result = method.invoke(target, args);
System.out.println("提交事务...");
}
return result;
}
}
);
return proxy;
}
}
/**
* 动态代理工厂类
* @author Wch
*
*/
class myProxyFactory{
private Object target;
public myProxyFactory(Object obj) {
this.target = obj;
}
public Object getProxyObj(){
Object proxyObj = Proxy.newProxyInstance(target.getClass().getClassLoader()
,target.getClass().getInterfaces()
, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//对指定方法进行代理加强
String name = method.getName();
return method.invoke(target, args);
}
});
return proxyObj;
}
}
Cglib代理
也叫”子类代理”,当目标对象没有实现接口,就不能使用jdk提供的代理,可以以子类的方式实现!
在运行时期动态在内存中构建一个子类对象的方法,从而对目标对象扩展,这种就是cglib代理!
在SpringAop编程中,
如果目标对象有实现接口,spring使用jdk提供的代理生成代理对象!
如果目标对象没有实现接口,使用cglib代理!
如果目标没有实现接口、且为final , 不能进行aop编程,报错!不能生成代理!
AOP编程
Aspect Oriented Programming
面向切面编程,分离业务代码和关注点代码,在业务代码中动态的植入关注点。
关注点:重复执行的代码,就称为关注点代码。
切面:关注点代码形成的类,就叫做切面。
个人理解:面向切面编程是为了提高代码的复用性,利用代理和反射在执行目标代码之前或之后执行切面代码。分离了业务代码和关注点代码,使得用户能够更好的关注逻辑代码的编写和更好的进行分离维护。
举例说明哪些是切面?
事务、权限控制、日志等
切入点表达式:拦截方法,给方法所在的类,生成代理对象(若类实现了接口:$proxyXXX;继承:cglib代理对象)
Spring在初始化容器的时候,会根据切入点表达式的规则,对复合拦截规则的类生成代理对象。
reference:
http://blog.csdn.net/Intlgj/article/details/5671248
使用注解实现AOP编程的步骤
引入aop 相关 jar文件
(aspectj 是在spring之前,面向切面开发的公用组件)
aopalliance.jar 【spring-framework-2.5.6\lib\aopalliance】
aspectjrt.jar 【spring-framework-2.5.6\lib\aspectj】
aspectjweaver.jar 【spring-framework-2.5.6\lib\aspectj】
spring-aop-3.2.5.RELEASE.jar 【Spring3.2源码】引入aop名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.ustb.e_aop_anno"></context:component-scan>
<!-- 开启aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
开启aop注解
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
使用注解实现AOP编程
/*
@Aspect 指定一个类为切面类
(切面类也需要实例化)
(切面类中的方法,也叫做通知)
@Before 前置通知 【在执行目标对象方法之前执行】
@After 后置通知 【在执行目标对象方法之后执行】
@AfterReturning 返回后通知 【在执行目标对象方法结束后执行, 出现异常不执行】
@AfterThrowing 异常通知 【在执行目标对象方法出现异常时候执行】
@Around 环绕通知 【环绕目标方法执行】
@Pointcut 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可)
*/
// 重复代码
@Component("aop")
@Aspect // 指定一个类为切面类
public class TransactionAop {
// 定义一个切入点表达式变量 (后面使用这个切入点表达式的时候,直接引用方法名即可)
@Pointcut("execution(* cn.ustb.e_aop_anno.UserDao.*(..))")//表示拦截userDao的所有方法呀
public void pointcut_(){
}
//【前置通知】
// 在执行业务方法,之前执行
@Before("pointcut_()")
public void beginTransaction() {
System.out.println("[前置通知] 开启事务..");
}
//【后置通知】
// 在执行业务方法,之后执行
@After("pointcut_()")
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
// 【返回后通知】 在执行目标方法结束后执行, 出现异常不会执行
@AfterReturning("pointcut_()")
public void afterReturing(){
System.out.println("[返回后通知]");
}
// 【异常通知】 在执行目标方法的时候出现异常执行
@AfterThrowing("pointcut_()")
public void afterThrowing(){
System.out.println("[异常通知]");
}
// 【环绕通知】 会环绕目标方法执行
@Around("pointcut_()")
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
- 测试方法:
public class App {
private ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",getClass());
// jdk代理
@Test
public void testApp() throws Exception {
// springIOC容器中获取对象,用接口接收!
IUserDao userDao = (IUserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
// springIOC容器中获取对象,用实现接收? 报错!!!!!!!!!
/*
* java.lang.ClassCastException:
* $Proxy13 cannot be cast to cn.ustb.e_aop_anno.UserDao
*/
// 总结:在spring的aop编程中,符合切入点表达式的目标类, 如果目标对象有实现接口,从容器获取对象的时候,一定要通过接口接收!
// 否则,包类型转换错误!
// UserDao userDao = (UserDao) ac.getBean("userDao");
// System.out.println(userDao.getClass());
// userDao.save();
}
// cglib代理
@Test
public void testApp_cglib() throws Exception {
UserDao userDao = (UserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
// 没有生成代理对象,因为没有被切入点表达式拦截
// execution(* cn.ustb.e_aop_anno.UserDao.*(..))
@Test
public void testApp_save_order() throws Exception {
OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}
使用XML配置实现AOP编程
- 导包(4个包),同注解方式一样
- 配置XML,注意几个重要的节点参数。
<aop:config>
、<aop:pointcut>
、<aop:aspect>
以及要声明aop名称空间
<?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: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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- dao实例加入容器 -->
<bean id="userDao" class="cn.ustb.f_aop_xml.UserDao"></bean>
<!-- 实例化切面类 -->
<bean id="aop111" class="cn.ustb.f_aop_xml.TransactionAop"></bean>
<!-- Aop相关配置 -->
<aop:config>
<!-- 切入点表达式定义 -->
<!-- *表示拦截了UserDao中的所有方法了 -->
<aop:pointcut expression="execution(* cn.ustb.f_aop_xml.UserDao.*(..))" id="pt"/>
<!-- 切面配置 -->
<aop:aspect ref="aop111">
<!-- 【环绕通知】 -->
<aop:around method="arroud" pointcut-ref="pt"/>
<!-- 【前置通知】 在目标方法之前执行 -->
<aop:before method="beginTransaction" pointcut-ref="pt" />
<!-- 【后置通知】 -->
<aop:after method="commit" pointcut-ref="pt"/>
<!-- 【返回后通知】 -->
<aop:after-returning method="afterReturing" pointcut-ref="pt"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
- 定义切面类,定义通知(方法)
// 切面类
public class TransactionAop {
public void beginTransaction() {
System.out.println("[前置通知] 开启事务..");
}
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
public void afterReturing(){
System.out.println("[返回后通知]");
}
public void afterThrowing(){
System.out.println("[异常通知]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
- 测试
// 切面类
public class TransactionAop {
public void beginTransaction() {
System.out.println("[前置通知] 开启事务..");
}
public void commit() {
System.out.println("[后置通知] 提交事务..");
}
public void afterReturing(){
System.out.println("[返回后通知]");
}
public void afterThrowing(){
System.out.println("[异常通知]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[环绕前:]");
pjp.proceed(); // 执行目标方法
System.out.println("[环绕后:]");
}
}
Spring生成代理对象的过程?
- 创建容器对象的时候, 根据“切入点表达式”拦截的类,生成代理对象;(在创建容器的时候就生成了代理对象了)
- 如果目标对象有实现接口,使用jdk代理!
- 如果目标对象没有实现接口,使用cglib代理!
- 从容器获取代理后的对象
- 执行代理对象的方法,在运行时期,动态植入“切面”类中的“通知”!
切入点表达式语法详解
切入点表达式:
拦截指定的类,生成代理对象!
execution(
modifiers-pattern? ————–拦截的方法的访问修饰符
ret-type-pattern ————–方法返回类型,必须指定
declaring-type-pattern? ————–拦截的方法所在的类
name-pattern(param-pattern)————–拦截的方法(以及方法的参数列表)
throws-pattern?————– 方法声明的异常
)
示例:
execution(* cn.ustb.f_aop_xml.UserDao.*(..))
execution(* cn.ustb.e_aop_anno.UserDao.*(..))
<!-- 开启注解扫描 -->
<context:component-scan base-package="cn.ustb.g_execution"></context:component-scan>
<!-- dao实例加入容器 -->
<bean id="userDao" class="cn.ustb.g_execution.UserDao"></bean>
<!-- 实例化切面类 -->
<bean id="aop" class="cn.ustb.g_execution.TransactionAop"></bean>
<!-- Aop相关配置 -->
<aop:config>
<!-- 切入点表达式定义 -->
<!-- 1. 拦截指定的方法(通用) -->
<!--<aop:pointcut expression="execution(* cn.ustb.g_execution.UserDao.save(..))" id="pt"/>-->
<!-- 2. 拦截指定的类下所有的方法 -->
<!--<aop:pointcut expression="execution(* cn.ustb.g_execution.UserDao.*(..))" id="pt"/>-->
<!-- 3. 拦截指定包下所有的类的所有方法 -->
<!--<aop:pointcut expression="execution(* cn.ustb.g_execution.*.*(..))" id="pt"/>-->
<!-- 3. 拦截指定包,以及其子包下所有类的所有方法 -->
<!----><aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>
<!-- 5. 拦截所有的public方法 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
<!-- 6. 拦截所有的包含save方法 -->
<!--<aop:pointcut expression="execution(* *save*(..))" id="pt"/>-->
<!-- 7. 拦截UserDao.save()方法与OrderDao.save() -->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) || execution(* cn..OrderDao.save(..))" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) or execution(* cn..OrderDao.save(..))" id="pt"/>-->
<!-- 8. 不拦截UserDao.save()方法 -->
<!--<aop:pointcut expression="!execution(* cn..UserDao.save(..))" id="pt"/>-->
<!--<aop:pointcut expression=" not execution(* cn..UserDao.save(..))" id="pt"/>-->
<!-- 9. 拦截UserDao.save()同时拦截OrderDao.save() -->
<!-- 注意: 这种很少用,一般都是或者的关系即: || 、 or -->
<!--<aop:pointcut expression="execution(* cn..UserDao.save(..)) and execution(* cn..OrderDao.save(..))" id="pt"/>-->
<!-- <aop:pointcut expression="execution(* cn..UserDao.save(..)) && execution(* cn..OrderDao.save(..))" id="pt"/>
-->
<!-- 切面配置 -->
<aop:aspect ref="aop">
<aop:around method="arroud" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
总结:
拦截,一定要指定到方法!
Spring 对jdbc模块的支持
到目前为止学习了Spring的功能有:
Spring Core ioc容器
Spring Web 对web支持(与struts整合)
Spring Aop 面向切面编程
Spring Jdbc 对jdbc的支持
Spring对JDBC的操作进行了简化,提供了JdbcTemplate模板工具类,简化Jdbc的操作!
import org.springframework.jdbc.core.JdbcTemplate;
封装了Jdbc操作,将其当成Jdbc中的statement对象进行数据库的操作。
步骤
导包
- 导入对Jdbc支持的jar包
spring-jdbc-3.2.5.RELEASE.jar 工具类包
spring-tx-3.2.5.RELEASE.jar 事务支持依赖包
c3p0-0.9.1.2.jar 连接池包
mysql-connector-java-5.1.12-bin.jar 数据库驱动包
配置
- 加载
db.properties
配置,创建database的bean
,创建JdbcTemplate
的bean将其注入。
<!-- 加载Proerties配置文件 -->
<context:property-placeholder location="classpath:cn/ustb/h_jdbc/db.properties"/>
<bean id="propertyConfigure" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:cn/ustb/h_jdbc/db.properties</value>
</list>
</property>
</bean>
<!-- 1. 实例化连接池 -->
<!-- 利用${}可以取出db.properties中的数据 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<property name="initialPoolSize" value="${initialPoolSize}"></property>
<property name="maxPoolSize" value="${maxPoolSize}"></property>
<property name="acquireIncrement" value="${acquireIncrement}"></property>
</bean>
<!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 实例化dao -->
<bean id="deptDao" class="cn.ustb.h_jdbc.DeptDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
db.properties
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///hib_demo
user=root
password=root
initialPoolSize=3
maxPoolSize=6
acquireIncrement=2
编写CRUD代码
- 在Java代码中通过JdbcTemplate对象对数据库进行CRUD操作。
public class DeptDao implements IDeptDao {
// 接收容器注入的JdbcTemplate对象
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// 1. 原始jdbc代码
public void save(Dept dept) {
jdbcTemplate.update("insert into t_dept(deptName) values(?)", dept.getName());
}
@Override
public void delete(Serializable id) {
jdbcTemplate.update("delete from t_dept where id=?", id);
}
@Override
public void update(Dept dept) {
jdbcTemplate.update("update t_dept set deptName=? where id=?", dept.getName(),dept.getId());
}
@Override
public Dept findById(Serializable id) {
// queryForList 把每一行都封装为map对象,再添加到list中
// List<Map<String, Object>> list = jdbcTemplate.queryForList("select * from t_dept");
// 传入类型参数,表示查询的列的类型; 这里只能查询一列
// List<String> list = jdbcTemplate.queryForList("select deptName from t_dept", String.class);
List<Dept> list = jdbcTemplate.query("select * from t_dept where id=?", new MyRowMapper(), id);
return (list!=null&&list.size()>0)?list.get(0):null;
}
@Override
public List<Dept> getAll() {
List<Dept> list = jdbcTemplate.query("select * from t_dept", new MyRowMapper());
return list;
}
// 封装Springjdbc查询的结果集
class MyRowMapper implements RowMapper<Dept>{
// 如何解析一行
@Override
public Dept mapRow(ResultSet rs, int rowNum) throws SQLException {
Dept dept = new Dept();
dept.setId(rs.getInt("id"));
dept.setName(rs.getString("deptName"));
return dept;
}
}
class MyRowMapo implements RowMapper<Dept>{
@Override
/**
* 如何解析一行
*/
public Dept mapRow(ResultSet rs, int rowNum) throws SQLException {
Dept a = new Dept();
a.setName(rs.getString("name"));
a.setId(rs.getInt("id"));
return a;
}
}
}
JdbcTemplate的API
见上面的代码,大部分的API已经囊括在内了。
reference:
http://blog.csdn.net/u011637069/article/details/50838630