Java框架之Spring(四)

本文主要讲述在Spring中

1 注解方式装配

2 以自动扫描把组件纳入spring容器中管理

3 面象切面编程-代理的jdk 版实现

4 使用 Cglib 生成代理

5 aop编程的一些概念

6 使用 Spring 创建代理对象(注解方式)

7 使用 Spring 创建代理对象(基于XML配置的方式)

8 关于Spring JDBC

一、注解方式装配

1) 导入jar包

2) 引入名称空间 Spring-context

<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"  //这里
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> //这里

3) 加入 <context:annotation-config />  //把spring 针对注解的处理器注册到 Spring中

4) 配置文件

<context:annotation-config />
<bean name="userAction_name" class="cat.action.UserAction" ></bean>
<bean name="userDao_name" class="cat.dao.UserDaoImpl"></bean>

5) UserAction

public class UserAction {
                        @Resource(name="userDao_name") //这里的 userDao_name 这个名字,是spring管理的beans的名字
                        private IUserDao dao;  //可以不用get 和 set 方法
                        
                        public void exectue(){
                            dao.addUser();
                            dao.delUser();
                            dao.getUser();
                        }            
                    }

说明:

@Resource 是 javax.annotation 包下的

@Resource 默认按名称装配,找不到的时候,再按类型装配 (前题是不指定name属性的时候),如果指定了name属性,则完全按名称匹。

也可以把这个注解,写在set 方法上

@Autowired   //它是org.springframework.beans.factory.annotation 包下的它默认是按类型匹配

@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:   

Autowired  @Qualifier("userDao_name")

二、以自动扫描把组件纳入spring容器中管理

1) 在配置文件中加入

<context:component-scan base-package="cat" />//将cat 包,及子包下的bean 进行扫描

注意.加入它,就可以把     <context:annotation-config /> 去掉了,因为它包含了这个注解的内容

它扫描带用  @Controller ,@Service , @Repository, @Component  注解的类

@Controller 控制层

@Service 业务层

@Repository 数据访问层

@Component 其他

2)在要被spring管理的bean上,加上注解

@Controller("userAction")   //可以指定它的名字,默认就是类名,首字母小写
public class UserAction {
@Resource(name="userDaoImpl")
private IUserDao dao;  //可以不用get 和 set 方法
                    
public void exectue(){
                    dao.addUser();
                    dao.delUser();
                    dao.getUser();
                    }
                }
                
@Repository
public class UserDaoImpl implements IUserDao {
                        ..
}

经过以上配置,配置文件中,就不用声明任何 bean 了。

三、面象切面编程-代理的jdk 版实现

需求

1) 拦截所有的业务方法

2) 判断用户是否有权限(以user是否为null 为准)

3) 有权限,就可以执行业务方法,没有则不执行

横切性关注点

1) 接口

public interface IUserDao {
                    void getUser();
                    void delUser();
                    void addUser();
                    void updateUser();
                }

2)实现类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserDaoImpl implements IUserDao {
public UserInfo user;  //如果它为null,则下面的方法没有权限执行
public void getUser() {
                        System.out.println("getUser方法被调用了");
                        System.out.println(user);
                    }
                
public void delUser() {
                        System.out.println("delUser方法被调用了");
                        System.out.println(user);
                    }
                
public void addUser() {
                        System.out.println("addUser方法被调用了");
                        System.out.println(user);
                    }
                
public void updateUser() {
                        System.out.println("updateUser方法被调用了");
                        System.out.println(user);
                    }
                }

3)代理工厂

//用来生成代理对象
public class MyProxyFactory implements InvocationHandler
                {
                    private Object targetObj ; //要被代理的目标对象
                    public Object createProxyInstance(Object targetObj){
                        this.targetObj=targetObj;
                        
                        //下面是产生代理对象
                        //ClassLoader loader 目标对象的类装载器
                        //Class<?> [] interfaces  目标对象的接口
                        //InvocationHandler h 处理器
                        return Proxy.newProxyInstance(this.targetObj.getClass().getClassLoader(), this.targetObj.getClass().getInterfaces(),this );
                    }
                    
                    
                    //proxy 代理对象
                    //method 指被调用的方法
                    //args method 所对应的这个方法的参数
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        Object result= null; //指的是执行完被代理的方法后的返回值
                        UserDaoImpl daoImpl=(UserDaoImpl)this.targetObj;
                        if(daoImpl.user!=null){
                            method.invoke(this.targetObj, args);
                        }
                        else{
                            System.out.println("方法"+method.getName()+" 因为没有权限被拦截了");
                        }
                    
                        return result;
                    }
                        
                }
        
                 public static void main(String[] args) {
                            MyProxyFactory proxyFactory=new MyProxyFactory();
                            
                            UserDaoImpl  daoImpl=new UserDaoImpl();
                            
                            UserInfo user=new UserInfo();
                            user.setId(90);
                            user.setUserName("张三");
                            user.setPassword("admin");
                            
                            daoImpl.user=user;
                            
                            IUserDao  dao= (IUserDao)proxyFactory.createProxyInstance(daoImpl);
                    
                            dao.addUser();
                            dao.updateUser();
                            dao.delUser();
                            dao.getUser();
                    }

附:

对上例的重要说明,如果将上例中的接中的方法声明为带有返回值的,比如 int addUser, int delUser,等,会发现它在执行的时候报空指针异常,这是因为在代理中,判断user为空的时候,没有执行     result=method.invoke 方法,所以也就拿不到返回值,对于void的返回的值的还没有问题,但对于指定返回值的,就出现问题,我们可以进行一下处理。

else{
     if(method.getName.equals("addUser"))
     result=9999   //意思是说,这个方法没有被调用,在程序中一定要用它的返回值是不对的,但我们可以给它指定一个
    }

说明: 使用jdk版的,被代理的对象一定要实现接口。

四、使用 Cglib 生成代理

1) 导入 cglib-nodep-2.1_3.jar 

2) 代理工厂

public class CGlibProxyFactory implements MethodInterceptor {
                private Object targetObj; //目标对象
                
                public Object createProxyInstance(Object targetObj){
                    this.targetObj=targetObj;
                    Enhancer enhancer=new  Enhancer(); //它是cglib提供的
                    enhancer.setSuperclass(this.targetObj.getClass()); 
                    enhancer.setCallback(this);
                    return enhancer.create();        
                }
            
                //obj 代理对象
                //method 拦截到的方法
                //args 方法的参数
                //arg3 方法的代理对象
                public Object intercept(Object obj, Method method, Object[] args,
                        MethodProxy arg3) throws Throwable {
                    Object result=null;
                        
                    UserDaoImpl daoImp=(UserDaoImpl)this.targetObj;
                    if(daoImp.user!=null){
                        result=arg3.invoke(this.targetObj,args);
                    }
                    else{
                        System.out.println("方法"+method.getName()+"由于没有权限,被拦截了");
                    }
                    return result;
                }
                
            }


            public static void main(String[] args) {
                    UserDaoImpl daoImp=new UserDaoImpl();
                    CGlibProxyFactory factory=new CGlibProxyFactory();
                
                
                    UserInfo user=new UserInfo();
                    user.setId(90);
                    user.setUserName("张三");
                    user.setPassword("admin");
                    daoImp.user=user;
                    
                    //使用cglib,被代理的类,可以不用实现接口
                    UserDaoImpl  dao=(UserDaoImpl)factory.createProxyInstance(daoImp) ;
                    //IUserDao dao=(IUserDao)factory.createProxyInstance(daoImp) ;  被代理的类,也可以实现接口
            
                    dao.addUser();
                    dao.getUser();
                    dao.updateUser();
                    int result=dao.delUser();
                    System.out.println("delUser的返回值是 " +result);
                    
                }

五、aop编程的一些概念

1.Aspect 切面

横切性关注点的抽象, 与类相似,类是对具体特征的抽象,切面,是对横切性关注点的抽象

2.joinpoint 连接点

指被拦截到的点,在spring中,这些点指的是方法,比如addUser, getUser 等

3.PointCut 切入点

对哪些连接点进行拦截的定义,例如上例 对所有的方法进行拦截,这就是一个切入点

4.Advice 通知

拦截到 joinpoint 之后要做的事,有前置通知,后置通知,异常通知(例外通知),最终通知,环绕通知

5.Target

要代理的目标对象

6.Weave 织入

将 Aspect(切面) 应用到 Target(目标对象),并导致proxy 对象创建的过程

7.Introduction 引入

在不修改类代码的情况, Introduction 可以在运行期动态的给类添加一些功能,或方法或字段,实际上,这些添加不是添到被代理的目标对象上,而是在生成的代理对象中添加的,对用户来说,感觉上好象原来这些类就有这些功能一样。

Spring 创建代理对象 , 也是根据上午说的两种方法进行的(jdk版的 或 cglib版的),它会根据被管理的对象是否实现了接口 ,如果实现了接口,它用 jdk版的,否则用 cglib版的

附: aop框架在什么时候去修改被代理的方法

有两个时机:

1.编译java代码的时候 (编译时增强) 以 AspectJ 代表

2.在运行时,动态的修改类 (生成新类,称为运行时增强,需要cglib,javassist) ,以SpringAOP 为代表

对于运行期增强来说,每次运行的时候,都要额外的字节码增强,这就需要额外的开销了

六、使用 Spring 创建代理对象(注解方式)

Sprinng 提供了两种方式创建代理对象

1)基于注解的方式

2)基于xml的方式

步骤

1) 导入jar

1.使用 Spring 进行 Aop 开发需要导入的 jar (这是以前的spring 2.5)

lib/aspect/aspect.jweaver.jar

lib/aspctejr.jar

lib/cglib-nodep-2.1_3.jar

lib/j2ee/common-annotation.jar

2.spring-aop-3.2.4.RELEASE.jar 必用.要不然会说什么无法读取方案文档 'http://www.springframework.org/schema/aop/spring-aop-3.2.xsd',

原因为

1) 无法找到文档;

2) 无法读取文档;

3) 文档的根元素不是 <xsd:schema>。

com.springsource.org.aopalliance-1.0.0.jar 为网上下载的

aspectjlib.jar

aspectjrt.jar 这两个是为了手写面向切面用到的

aspectjweaver-1.6.9.jar 必用,是从Myeclipse自动的创建的项目中来的

2) 引入名称空间 spring-aop

xmlns:aop="http://www.springframework.org/schema/aop"
....
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd     "
....

3) 打开配置节

<aop:aspectj-autoproxy />

4) 声明一个切面

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
                
//声明一个切面
@Aspect @Component   //不要忘了加上  @Component
public class MyInterceptor {
                    
@Pointcut("execution(* cat.dao.UserDaoImpl.*(..))") //切入点
private void anyMethod(){} //这是给切入点定义一个名称,看起来怪怪的,但就这么写
                
//前置通知
@Before("anyMethod()")  //切入点的名字千万不要忘加()
public void beforeDevice(){
                        System.out.println("前置通知触发了");
                    }
                    
//后置通知
@AfterReturning("anyMethod()")
public void afterDevice(){
                        System.out.println("后置通知触发了");
                    }
                    
//最终通知
@After("anyMethod()")
public void finallDevice(){
                        System.out.println("最终通知触发了");
                    }
                    
//例外通知
@AfterThrowing("anyMethod()")
public void execptionDevice(){
                        System.out.println("例外通知触发了");
                    }
                }

关于切入点的说明: @Pointcut("execution(* cat.dao.UserDaoImpl.*(..))")

execution () 表示业务方法执行的时候进行拦截

*  表示任意的方法返回值类型

* cat.dao.UserDaoImpl.* 对该类下的所有的方法都进行拦截

(..) 表示不论方法的参数是什么样的都拦截

另外,可以使用 || 进行多条件的连接

@Pointcut("execution(* cat.dao.impl.UserDaoImpl.add*(..))||"   过滤所有add打头的方法
                 + "execution(* cat.dao.impl.UserDaoImpl.upd*(..))||"  过滤所有的upd打头的方法
                 + "execution(* cat.dao.impl.UserDaoImpl.del*(int,String))" 
//UserDaoImpl 类 :
                    
@Repository("userDaoImpl")
public class UserDaoImpl implements IUserDao {
public void getUser() {
                            System.out.println("getUser方法被调用了");
                        }
                    
public int delUser() {
                            System.out.println("delUser方法被调用了");
                            return 2;
                        }
                    
public void addUser() {
                            System.out.println("addUser方法被调用了");
                        }
                    
public void updateUser() {
                            System.out.println("updateUser方法被调用了");
                        }
                    }
//测试
public static void main(String[] args) {
                        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
                        IUserDao dao=(IUserDao)ctx.getBean("userDaoImpl");
                        dao.getUser();
                        dao.addUser();
                        dao.delUser();
                    }

附加说明:

对于通知,可以声明传入 JoinPoint 类型的参数,由这个参数可以取得被代理的目标对象。

@Before("anyMethod()") 
public void beforeDevice(JoinPoint point){
                    UserDaoImpl daoImpl=(UserDaoImpl)point.getTarget();  //得到被代理的目标对象
                    daoImpl.test(); //调用目标对象中的其他方法,或访问其属性
                    System.out.println(daoImpl); //cat.dao.UserDaoImpl@1c3590e
                    System.out.println("前置通知触发了");
                }
//环绕通知
                @Around("anyMethod()")
                public Object aroundDevice(ProceedingJoinPoint point){
                    Object result= null;
                    System.out.println("环绕通知,目标方法调用之前");
                    
                    try{
                        result=point.proceed(); //放行(执行被拦到的方法)
                    }
                    catch(Throwable ex){
                        System.out.println("环绕通知,例外通知的代码被调用了");
                    }
                    finally{
                        System.out.println("环绕通知,最终通知的代码被调用了");
                    }
                    
                    System.out.println("环绕通知,后置通知的代码被调用了");
                    
                    return result;     
                }

方法的参数和返回值等

1) 取得被拦截到的方法的参数

@Before("anyMethod() && args(userName,password)")  //切入点的名字千万不要忘加()
public void beforeDevice(String userName,String password){
                        
                        System.out.println("userName="+userName);
                        System.out.println("password="+password);
                        
                        System.out.println("前置通知触发了");
                    }

2) 取得执行的函数的返回值

@AfterReturning(pointcut="anyMethod()",returning="resultAAA") 
public void afterDevice(int resultAAA){  //取得返回值
                    System.out.println("返回值是"+resultAAA);
                    System.out.println("后置通知触发了");
                }

3) 取得异常信息

@AfterThrowing(pointcut="anyMethod()",throwing="ex")
public void execptionDevice(Exception ex){
                    System.out.println("异常信息是:"+ex.getMessage());
                    System.out.println("例外通知触发了");
                }

七、使用 Spring 创建代理对象(基于XML配置的方式)

1)创建切面

@Component("myInterceptor2")
public class MyInterceptor2 {
// 前置通知
public void beforeDevice() {
                        System.out.println("前置通知触发了");
                    }
                
// 后置通知
public void afterDevice() {
                        System.out.println("后置通知触发了");
                    }
                
// 例外通知
public void execptionDevice() {
                        System.out.println("例外通知触发了");
                    }
                
// 最终通知
public void finallDevice() {
                        System.out.println("最终通知触发了");
                    }
                
// 环绕通知
public Object aroundDevice(ProceedingJoinPoint point) {
                        Object result = null;
                        System.out.println("环绕通知,目标方法调用之前");
                
                        try {
                            result = point.proceed(); // 放行(执行被拦到的方法)
                        } catch (Throwable ex) {
                            System.out.println("环绕通知,例外通知的代码被调用了");
                        } finally {
                            System.out.println("环绕通知,最终通知的代码被调用了");
                        }
                
                        System.out.println("环绕通知,后置通知的代码被调用了");
                
                        return result;
                    }
                }

2)配置文件

<context:component-scan base-package="cat" />    
                <aop:aspectj-autoproxy />
                
                <aop:config>
                    <aop:aspect id="aspectAAA" ref="myInterceptor2" >
                        <aop:pointcut expression="execution(* cat.dao.UserDaoImpl.*(..))" id="HHH"/>
                        <aop:before pointcut-ref="HHH"  method="beforeDevice" />
                        <aop:after-returning pointcut-ref="HHH"  method="afterDevice" />
                        <aop:after-throwing pointcut-ref="HHH"  method="execptionDevice" />
                        <aop:after pointcut-ref="HHH"  method="finallDevice"  />
                        <aop:around pointcut-ref="HHH"  method="aroundDevice"  />
                    </aop:aspect>
                </aop:config>

关于表达式: execution(* cat.dao.impl.UserDaoImpl.*(..))

1 只拦截返回类型为String 的函数

execution(java.lang.String cat.dao.impl.UserDaoImpl.*(..))  //这里前面不写java.lang.也可以

2 匹配第一个参数是String,其他参数怎么样都行的

execution(* cat.dao.impl.UserDaoImpl.*(String,..))

3 匹配返回值不是String型的

execution(!String cat.dao.impl.UserDaoImpl.*(..))

4 匹配cat.dao及其子包下的

execution(java.lang.String cat.dao..*(..))

Spring 会对所有满足表达式的对象创建代理对象, 如果Spring 发现这些对象实现了接口,它会使用proxy类(JDK中的)创建。反之,它会使用 CGlib 类来创建代理对象

附: Spring execution 表达式

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外,所有的部分都是可选的。 返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是 *,它代表了匹配任意的返回类型。

一个全称限定的类型名将只会匹配返回给定类型的方法。名字模式匹配的是方法名。 你可以使用 * 通配符作为所有或者部分命名模式。 参数模式稍微有点复杂:() 匹配了一个不接受任何参数的方法, 而 (..) 匹配了一个接受任意数量参数的方法(零或者更多)。 模式 (*) 匹配了一个接受一个任何类型的参数的方法。 模式 (*,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型。

下面给出一些常见切入点表达式的例子。

任意公共方法的执行:

execution(public * *(..))

任何一个以“set”开始的方法的执行:

execution(* set*(..))

AccountService 接口的任意方法的执行:

execution(* com.xyz.service.AccountService.*(..))

定义在service包里的任意方法的执行:

execution(* com.xyz.service.*.*(..))

定义在service包或者子包里的任意方法的执行:

execution(* com.xyz.service..*.*(..))

在service包里的任意连接点(在Spring AOP中只是方法执行) :

within(com.xyz.service.*)

在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) :

within(com.xyz.service..*)

实现了 AccountService 接口的代理对象的任意连接点(在Spring AOP中只是方法执行) :

this(com.xyz.service.AccountService)

'this'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得代理对象可以在通知体内访问到的部分。

实现了 AccountService 接口的目标对象的任意连接点(在Spring AOP中只是方法执行) :

target(com.xyz.service.AccountService)

'target'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得目标对象可以在通知体内访问到的部分。

任何一个只接受一个参数,且在运行时传入的参数实现了 Serializable 接口的连接点 (在Spring AOP中只是方法执行)

args(java.io.Serializable

'args'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得方法参数可以在通知体内访问到的部分。 请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args只有在动态运行时候传入参数是可序列化的(Serializable)才匹配,而execution 在传入参数的签名声明的类型实现了 Serializable 接口时候匹配。

有一个 @Transactional 注解的目标对象中的任意连接点(在Spring AOP中只是方法执行)

@target(org.springframework.transaction.annotation.Transactional)

'@target' 也可以在binding form中使用:请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。

任何一个目标对象声明的类型有一个 @Transactional 注解的连接点(在Spring AOP中只是方法执行)

@within(org.springframework.transaction.annotation.Transactional)

'@within'也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。

任何一个执行的方法有一个 @Transactional annotation的连接点(在Spring AOP中只是方法执行)

@annotation(org.springframework.transaction.annotation.Transactional)

'@annotation' 也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。

任何一个接受一个参数,并且传入的参数在运行时的类型实现了 @Classified annotation的连接点(在Spring AOP中只是方法执行)

@args(com.xyz.security.Classified)

八、关于Spring JDBC

<?xml version="1.0" encoding="UTF-8"?>
<beans
      xmlns:aop="http://www.springframework.org/schema/aop"
      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"
      xsi:schemaLocation=
          "http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
            
<context:component-scan base-package="cat" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" ><!-- 单实例bean -->
          <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
          <property name="url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&amp;characterEncoding=UTF-8"/>
          <property name="username" value="root"/>
          <property name="password" value="root"/>      
          <property name="initialSize" value="10"/>   <!-- 连接池启动时的初始值 -->        
          <property name="maxActive" value="300"/>    <!-- 连接池的最大值 -->
          <property name="maxIdle" value="5"/> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
          <property name="minIdle" value="3"/> <!--  最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 -->
</bean>
              
</beans>

dao层代码:

package cat.dao;
        import java.sql.*;
        import java.util.List;
        import java.util.Map;
        import javax.annotation.Resource;
        import javax.sql.DataSource;
        import org.springframework.dao.DataAccessException;
        import org.springframework.jdbc.core.BeanPropertyRowMapper;
        import org.springframework.jdbc.core.ConnectionCallback;
        import org.springframework.jdbc.core.JdbcTemplate;
        import org.springframework.stereotype.Service;
        
        
        import cat.beans.UserInfo;
        
        @Service
        public class UserDaoImpl 
        {
            private JdbcTemplate  jdbcTemplate; //进行数据库操作的模板,它是线程安全的
        
            @Resource
            public void setDataSource(DataSource  dataSource) {
                this.jdbcTemplate = new JdbcTemplate(dataSource);
            }
        
            public int addUser(UserInfo user){
                String sql="insert into userInfo (userId,userName,password,note) values(?,?,?,?)";
                Object [] params={user.getUserId(),user.getUserName(),user.getPassword(),user.getNote()};    
                return jdbcTemplate.update(sql,params);
            }
            
            public int delUser(int id){
                String sql="delete  from userInfo where id=?";
                return jdbcTemplate.update(sql,id);
            }
            
            public int updateUser(UserInfo user){
                String sql="update UserInfo set userId=?,userName=?,password=?,note=? where id=?";
                Object [] params={user.getUserId(),user.getUserName(),user.getPassword(),user.getNote(),user.getId()};    
                return jdbcTemplate.update(sql,params);
            }
            
            //查询一个对象出来
            public UserInfo getUserById(int id){
                String sql="select * from userInfo where id=?"; //注意:如果查询结果多于一条,将出错
                Object [] params={id};
                UserInfo user=jdbcTemplate.queryForObject(sql, params,new BeanPropertyRowMapper<UserInfo>(UserInfo.class));
                return user;
            }
            
            public List<UserInfo> getUserList(){
                String sql="select * from userInfo";
                return jdbcTemplate.query(sql,new BeanPropertyRowMapper<UserInfo>(UserInfo.class));
            }
            
            public int getUserCount(){
                String sql="Select count(*) from userInfo";
                return jdbcTemplate.queryForInt(sql);
        
            }
            
            public String getUserNameById(int id){
                String sql="select userName from userInfo where id="+id;
                return jdbcTemplate.queryForObject(sql, String.class);//因为要被查询出来的字段是String型的所以这里用String.class
            }
            
            public Map<String,Object>getUserMap(int id){
                String sql="select * from userInfo where id="+id;
                return jdbcTemplate.queryForMap(sql);
            }
            
            public Integer addUserNew(final UserInfo user){ //这里必须为final  
                   return jdbcTemplate.execute(new ConnectionCallback<Integer>() {
                    public Integer doInConnection(Connection conn) throws SQLException,DataAccessException {
                        
                        String sql="insert into userInfo (userId,userName,password,note) values(?,?,?,?)";
                        PreparedStatement stm=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
                        
                        stm.setString(1, user.getUserId());
                        stm.setString(2, user.getUserName());
                        stm.setString(3, user.getPassword());
                        stm.setString(4, user.getNote());
                        
                        int result=stm.executeUpdate();
                        
                        ResultSet rsKey=stm.getGeneratedKeys();
                        if(rsKey.next()){
                            System.out.println("新添加的用户的主键是 "+rsKey.getInt(1));
                        }
                        
                        return result;
                    }
                });
                
            }    
        }

JUnit代码:

package cat.test;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
            
import cat.beans.UserInfo;
import cat.dao.UserDaoImpl;
            
public class JUnitTest2 {
            
                static ClassPathXmlApplicationContext  ctx;
                static UserDaoImpl dao;
                 
                @BeforeClass
                public static void setUpBeforeClass() throws Exception {
                    ctx=new ClassPathXmlApplicationContext("beans.xml");
                    dao=(UserDaoImpl)ctx.getBean("userDaoImpl");
                }
            
                @Test
                public void testAdd(){
                    UserInfo user=new UserInfo();
                    user.setUserName("姜浩");
                    user.setUserId("clientaaaa");
                    int result=dao.addUser(user);
                    System.out.println(result);
                    
                }
                @Test
                public void test_delUser(){
                    dao.delUser(32768);
                }
                
                @Test
                public void test_getUserById(){
                    UserInfo user=dao.getUserById(98309);
                    System.out.println(user);
                }
                
            
                @Test
                public void test_updateUser(){
                    UserInfo user=dao.getUserById(98309);
                    user.setUserName("新名字");
                    System.out.println(dao.updateUser(user));
                }
                
                @Test
                public void test_getUserList(){
                    List<UserInfo>userList=dao.getUserList();
                    
                    for(UserInfo u:userList){
                        System.out.println(u);
                    }
                }
                
                @Test
                public void test_getUserCount(){
                    int result=dao.getUserCount();
                    
                    System.out.println(result);
                }
                
                @Test
                public void test_getUserNameById(){
                    String result=dao.getUserNameById(98309);
                    
                    System.out.println(result);
                }
                
                @Test
                public void test_getUserMap(){
                    Map<String ,Object> map=dao.getUserMap(98309);
                    Set<Map.Entry<String,Object>> entrySet=map.entrySet();
                    Iterator<Map.Entry<String, Object>>it=entrySet.iterator();
                    while(it.hasNext()){
                        Map.Entry<String, Object> item=it.next();
                        System.out.println(item.getKey()+":"+item.getValue());
                    }
                }
                
                @Test
                 public void test_addUserNew(){
                    UserInfo user=new UserInfo();
                    user.setUserName("姜浩二号");
                    user.setUserId("clientaaaa");
                    int result=dao.addUser(user);
                    System.out.println(result);
                    
                    int r2=dao.addUserNew(user);    
                    System.out.println("r2="+r2);
                }
            }

备注:

代理模式和装饰模式的区别

这两个设计模式看起来很像。

对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。我们可以用另外一句话来总结这些差别:

使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。

原文地址:https://www.cnblogs.com/1693977889zz/p/8157640.html