Spring(2)

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

面向切面编程,分离业务代码关注点代码,在业务代码中动态的植入关注点。

关注点:重复执行的代码,就称为关注点代码

切面:关注点代码形成的类,就叫做切面
enter image description here

个人理解:面向切面编程是为了提高代码的复用性,利用代理和反射在执行目标代码之前或之后执行切面代码。分离了业务代码和关注点代码,使得用户能够更好的关注逻辑代码的编写和更好的进行分离维护。

举例说明哪些是切面?
事务、权限控制、日志等

切入点表达式:拦截方法,给方法所在的类,生成代理对象(若类实现了接口:$proxyXXX;继承:cglib代理对象)

Spring在初始化容器的时候,会根据切入点表达式的规则,对复合拦截规则的类生成代理对象。

reference
http://blog.csdn.net/Intlgj/article/details/5671248

使用注解实现AOP编程的步骤
  1. 引入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源码】

  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>    
  1. 开启aop注解
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

  2. 使用注解实现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("[环绕后:]");
    }

}
  1. 测试方法:

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编程
  1. 导包(4个包),同注解方式一样
  2. 配置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> 
  1. 定义切面类,定义通知(方法)
// 切面类
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("[环绕后:]");
    }

}
  1. 测试
// 切面类
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生成代理对象的过程?
  1. 创建容器对象的时候, 根据“切入点表达式”拦截的类,生成代理对象;(在创建容器的时候就生成了代理对象了
  2. 如果目标对象有实现接口,使用jdk代理!
  3. 如果目标对象没有实现接口,使用cglib代理!
  4. 从容器获取代理后的对象
  5. 执行代理对象的方法,在运行时期,动态植入“切面”类中的“通知”!
切入点表达式语法详解

切入点表达式:
拦截指定的类,生成代理对象!

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(..)) &amp;&amp; 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对象进行数据库的操作。

步骤
导包
  1. 导入对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 数据库驱动包
配置
  1. 加载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代码
  1. 在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值