点石成金什么意思:Spring边学习边总结

来源:百度文库 编辑:中财网 时间:2024/04/29 06:42:03

1.Spring介绍:
    事务传播行为:多个业务bean方法要求在同一个事务中发生,如:
    public void pay()
    {
        bean1.update();
        bean2.save();
    } 解决方法是将conn对象传递给两个bean对象,这样就可以在同一数据库连接进行事务管理了:
     public void pay()
    {
        Connection conn=...
        conn.setAutoCommit(false);
        bean1.update(conn);
        bean2.save(conn);
    } 同时不管bean1.update()是否成功,要求bean2.save()不受影响,这些都可以利用spring来解决(声明式事务管理)

2.Spring配置:


       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

  //调用默认构造函数
   
 

id不能包含特殊字符,name可以包含特殊字符

junit测试方法:
@Test public void instanceSpring(){
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");//
  PersonService ps = (PersonService)applicationContext.getBean("personService");
  ps.save();
 }

3.Spring实例化bean的原理:

ClassPathXmlApplicationContext读取配置文件获得每一个需要实例化的bean的id和对应的类,在读取一个bean的信息后,会在容器中为它实例化一个带两个实例域的bean(private string id;private string class),并将所有装有所有bean信息的对应的bean都放入一个map内。在从该map里面取出信息bean,利用反射将bean实例化,存入容器,应用spring容器的程序就可以通过getBean方法来获得相应的bean.

4.三种实例化bean的方法:

上面一种;
静态工厂类实例化:
 
实例工厂类实例化:

 

 
 

5.bean的作用域:Singleton(同一实例) | Prototype(新实例)
PersonService ps1 = (PersonService)applicationContext.getBean("personService");
PersonService ps2 = (PersonService)applicationContext.getBean("personService");
System.out.println(ps1 == ps2);//true所以默认情况下容器为bean保存唯一实例
获得不同的实例:


6.bean的作用域:
实例化:默认(singleton情况下)是在spring容器实例化的时候也就是ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");的时候;但是如果这时设置了lazy_init="true"则会在getBean的时候实例化。;
prototype情况下:是在调用getBean的时候实例化.

7.为bean指定初始化和销毁执行的方法:
 

8.依赖对象的注入:PersonServiceBean 依赖 PesonDaoBean(PersonServiceBean的实例域)




9.依赖注入的原理:
spring为每一个bean的property创建一个保存信息的PropertyDefinitionBean,一个bean应该会对应多个PropertyDefinitionBean,所以可以在前面定义BeanDefinitionBean中用一个集合来存放多个PropertyDefinitionBean,当利用BeanDefinitionBean和反射实例化一个bean的时候,同时会根据BeanDefinitionBean中的PropertyDefinitionBean的集合来从容器中指定相应的所依赖的bean,因为比如说personDao这个bean已经在容器中实例化,不需要再实例化,只需要指定就可以了,再利用反射就可以对bean所依赖的对象进行注入了。过程是先实例化bean,再来为它注入对象,得到bean的所有属性信息,再与配置文件进行比较,如果配置文件中配置的属性在bean定义中不存在,就不会为它注入。注意
里面的name的名字必须与bean定义中的依赖对象名相同,否则会注入失败(private PersonDao personDao)。

10.注入方式:
上面一种;(可以被多个bean重用)
内部bean:(只能被单个的bean使用,估计是注入内部bean的时候没有将实例化的内部bean放入spring容器,只是一个临时对象bean)





为基本数据类型注入值:

   
    
   

   
   
   
 

集合类型的注入:一般需要在注入对象的定义中指定一个集合类,如:
Private Set sets = new HashSet();
Private List lists = new ArrayList();
Set集合类型注入:

   
    
     sets第一个
     sets第二个
     sets第三个
    

   

 

List集合类型注入:

   
    
     lists第一个
     lists第二个
     lists第三个
    

   

 

Properties类型注入:

   
    
     properties第一个
     properties第二个
     properties第三个
    

   

 

Map类型注入:

   
    
     
     
     
    

   

 

11.构造器注入:(前面用的都是setter方法注入)

  
    
 

注意这里的type因为构造器定义是
public PersonServiceBean(String name, PersonDao personDao) {
  this.name = name;
  this.personDao = personDao;
 }所以type不能指定为"com.zxf.dao.impl.PersonDaoBean"尽管personDao是"com.zxf.dao.impl.PersonDaoBean"的实例,但是子类引用不能指向父类对象。

13.使用注解进行注入:需要添加以下配置信息:
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-2.5.xsd ">
            

@Autowired默认按类型装配
@Autowired
 private UserDao userDao;//字段上添加注解
@Autowired
 public void setUserDao(UserDao userDao) {
  this.userDao = userDao;
 }//setter注入
也可以指定名称注入。不过只能应用于字段
@Autowired(required=true) @Qualifier("userDao")
private UserDao userDao;//required=true要求该依赖对象必须注入。
@Resource默认按名称装配,按名称无法装配的时候则按类型
@Resource
 private UserDao userDao2;//字段注入,然后会在配置文件中找名为userDao2的bean进行注入,否则会找UserDao类型的bean进行注入
@Resource(name="userDao_2")
 public void setUserDao2(UserDao userDao2) {
  this.userDao2 = userDao2;
 }//setter方法注入,可以指定name属性,用于寻找名为name 的bean
一般推荐用@Autowired进行注入!

13.注入方式:手工装配和自动装配
自动装配:
autodetect 自动装配。

14.通过利用classpath自动扫描方式把组件纳入容器管理:配置:

@Service--Service层
@Controller--控制层
@Component--一般组件
@Repository--DAO层
---------------------------------
@Service//("userService")//@Scope("prototype")
public class UserServiceBean implements UserService;
@Repository//("userDao")//@Scope("prototype")
public class UserDaoBean implements UserDao;
这样就已经将bean交给Spring管理了,Spring可以通过getBean()方法得到bean,默认的名称是类名的第一个字母变为小写就可以了,同时还可以像上面那样指定名称。由于默认的情况下,这些被实例化出来的bean的作用域是singleton的,所以需要额外的指定注解来改变作用域,如上面的@scope。

15.在Spring自动管理Bean的情况下,指定bean的初始化方法:
@PostConstruct
 public void init(){
  System.out.println("初始化方法开始");
 }
指定销毁方法:
@PreDestroy
 public void destroy(){
  System.out.println("关闭资源");
 }

16.J2SE动态代理实现拦截:目标对象必须实现接口,否则不能应用Proxy,比如
public class UserServiceBean implements UserService
代理类也会实现同样的接口,并且会在实现方法中调用目标对象相应的方法
public class JDKProxyFactory implements InvocationHandler {

 private Object targetObject;
 
 public Object createProxyObject(Object targetObject) {
  this.targetObject = targetObject;
  return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
    this.targetObject.getClass().getInterfaces(), this);
 }

 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  Object result = method.invoke(targetObject, args);
  return null;
 }
}//创建的代理对象中有一个实例域存放InvocationHandler 的对象;
当客户端调用代理对象时,代理对象会执行InvocationHandler对象的invoke方法,将方法调用委派给目标对象。
UserService userService = (UserService)factory.createProxyObject(new UserServiceBean("曾险峰"));
  userService.save("Teeny");
上述代码返回的是一个实现了UserService接口的代理对象,通过它来调用目标对象方法时,会调用InvocationHandler.invoke()方法,可能是如下形式
public void save() {
    InvocationHandler.invoke(this,savemethod,args);
}//maybe...
注:利用动态代理要求目标对象必须实现接口,假如目标对象没有实现接口,则应该用cglib来实现。

17.CGLIB实现拦截:不要求目标对象实现接口,代理对象创建的时候继承目标对象,并且覆盖目标对象所有非final的方法,当利用代理对象调用目标对象的方法时,会调用MethodInterceptor.interceptor()方法,在该方法里面做权限判断,权限通过时再将方法调用委派给目标对象。
private Object targetObject;
 
 public Object createProxyObject(Object targetObject) {
  this.targetObject = targetObject;
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(this.targetObject.getClass());//继承了目标类,并且覆盖目标类所有非final的方法
  enhancer.setCallback(this);
  return enhancer.create();
 }

 public Object intercept(Object proxy, Method method, Object[] args,
   MethodProxy methodProxy) throws Throwable {
  UserServiceBean userService = (UserServiceBean)this.targetObject;
  Object result = null;
  if(userService.getUser() != null) {
   result = methodProxy.invoke(targetObject, args);
  }
  return result;
 }
同时因为代理对象不需要目标对象实现接口,所以要用目标对象类直接饮用代理对象:
UserServiceBean userService = (UserServiceBean)factory.createProxyObject(new UserServiceBean("曾险峰"));
  userService.save("Teeny");

18.各种通知:
public Object intercept(Object proxy, Method method, Object[] args,
   MethodProxy methodProxy) throws Throwable {
  UserServiceBean userService = (UserServiceBean)this.targetObject;
  Object result = null;
//环绕通知
  if(userService.getUser() != null) {
    //前置通知
  try{
     result = methodProxy.invoke(targetObject, args);
    } catch(Exception e) {
    //例外通知
    } finally {
    //最终通知
    }
    //后置通知
  }
  return result;
 }

19.AOP概念:
切面(aspect):与类类似,不过类是对物体特征的抽象,切面是对横切性关注点的抽象,前面整个的JDKProxyFactory和CGLIBProxyFactory都可以看作是一个切面,是对客户端与bean之间一层拦截定义的抽象;
连接点(joinpoint):指的是被拦截到的点,在Spring中就是指方法,实际上还可以是域和构造器;
切入点(pointcut):指的是对joinpoint进行拦截的定义;
advice(通知):前有介绍;
目标对象(target);
织入(weave):将aspect应用到target对象并创建代理对象的过程;
引入(introduction):在不修改类得前提下,在运行期为为类动态添加一些域和方法

20.Spring进行aop编程:
注解方式:
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
      

@Aspect @Component//定义切面,并且将bean交给spring管理
public class MyInterceptor {

 // "execution (* com.zxf.service..*.*(..))"//
 @Pointcut("execution(* spring.aop.service.impl.UserServiceBean.*(..))")
 public void anyMethod() {//方法名就是切入点的名称
 }
 //定义切入点,括号内的表达式指定对哪些joincut进行拦截
 @Before("anyMethod() && args(username)")
 public void doAccessCheck(String username){
  System.out.println("前置通知");
 }
 //定义前置通知,指定参数,两个参数名必须一致,假如被拦截的方法没有匹配的参数则不会有前置通知,但是可以不与被拦截方法中的参数名一致,但三者类型应一致
 @AfterReturning(pointcut=anyMethod()",returning="result")
 public void doAfterReturning(String result) {
  System.out.println("后置通知" + result);
 }
 //定义后置通知,并将bean对象方法调用后的结果作为参数传入后置通知
 @After("anyMethod()")
 public void doAfter() {
  System.out.println("最终通知");
 }
 
 @AfterThrowing(pointcut="anyMethod()",throwing="e")
 public void doAfterThrowing(Exception e) {
  System.out.println("例外通知" + e.getMessage());
 }
 //定义例外通知,并将抛出的例外传给例外通知
 @Around("anyMethod()")
 public Object doAround(ProceedingJoinPoint pdj) throws Throwable {
  Object result = null;
  //if(){//权限判断
   System.out.println("环绕通知");
   result = pdj.proceed();
  //}
  return result;
 }//定义环绕通知,
}
基于xml方式进行aop编程:
       
      

      
      

       
      
        
         
         
         
         
         
         
        

      

MyInterceptor在这里只是一个普通的java类,没有使用任何注解。同时也可以把定义切面的类MyInterceptor和bean都用Spring来管理。名称为默认名称或自己在注解上指定的名称。
拦截表达式解释:
java.lang.String  spring.aop.service.impl.UserService2.*(java.lang.String , ..)
匹配spring.aop.service.impl.UserService2里面返回值类型为String,并且第一个参数类型为String的方法。
!void  spring.aop.service.impl.UserService2.*(java.lang.String , ..)
匹配spring.aop.service.impl.UserService2里面有返回值,并且第一个参数为String的方法
* spring.aop..*.*(..)
匹配spring.aop包以及它的子包下面所有类的所有方法。

21.Spring加jdbc集成开发:
基于注解的方式配置:

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context" 
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/context 
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
           //读取配置文件作为下面datasource的配置
          
           //注解方式注入对象
          
           //交给Spring自动管理
          
           //注册事务管理的处理器
          
           //${}读取配置文件参数,为属性注入值
          
            
                        value="${url}"/>
            
            
            
            
            
            
            
            
            
          

           
          
            
          

@Service @Transactional
public class UserServiceBean implements UserService //将bean交给spring 管理同时让spring来管理事务

注意:这里让UserServiceBean实现了UserService接口,所以在getBean的时候不能用
UserServiceBean us = (UserServiceBean)applicationContext.getBean("userServiceBean");而要用
UserService us = (UserService)applicationContext.getBean("userServiceBean");
假如不用spring自动管理而是手动配置bean则应该可以解决这个问题。
JUnitTest:
public class Test_006 {
 
 private static UserService us = null;
 
 @BeforeClass
 public static void beforClass() {
  try {
   ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
     "beans_5.xml");
   us = (UserService) applicationContext.getBean("userServiceBean");
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 @Test
 public void getUser(){
  System.out.println(us.getUser(1).getName());
 }
 
 @Test
 public void updateUser() {
  UserBean user = (UserBean)us.getUser(1);
  user.setName("Teeny");
  us.update(user);
 }
 
 @Test
 public void save() {
  UserBean user = new UserBean();
  user.setName("曾险峰");
  us.save(user);
 }
 
 @Test
 public void getAllUsers() {
  List users = us.getAllUsers();
  for(UserBean u : users){
   System.out.println(u.getName());
  }
 }
 
 @Test
 public void deleteUser() {
  us.delete(1);
 }
}
ServiceBean:
@Service @Transactional
public class UserServiceBean implements UserService {
 
 private JdbcTemplate jdbcTemplate;
 
 @Autowired
 public void setDataSource(DataSource dataSource) {
  this.jdbcTemplate = new JdbcTemplate(dataSource);
 }//setDataSource(DataSource dataSource)在Spring容器中选择dataSource的bean

 public void save(UserBean user) {
  this.jdbcTemplate.update("insert into T_Spring(name) values(?)", new Object[]{user.getName()},
    new int[]{java.sql.Types.VARCHAR});
 }
 
 public void update(UserBean user) {
  this.jdbcTemplate.update("update T_Spring set name=? where id=?", new Object[]{user.getName(),user.getId()},
    new int[]{java.sql.Types.VARCHAR,java.sql.Types.INTEGER});
 }
 
 public UserBean getUser(Integer id) {
  UserBean user = new UserBean();
  user = (UserBean)this.jdbcTemplate.queryForObject("select * from T_Spring where id=?", new Object[]{id},new UserRowMapper());
  return user;
 }
 
 public List getAllUsers() {
  List users = new ArrayList();
  users = (List)this.jdbcTemplate.query("select * from T_Spring", new UserRowMapper());
  return users;
  }
 
 public void delete(Integer id) {
  this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id},
    new int[]{java.sql.Types.INTEGER});
 }
}
利用Spring封装好的jdbcTemplate进行bean的操作,还须提供一个实现了RowMapper的类,作为记录的映射:
public class UserRowMapper implements RowMapper {

 public Object mapRow(ResultSet rs, int arg1) throws SQLException {
  UserBean user = new UserBean();
  user.setName(rs.getString("name"));
  user.setId(rs.getInt("id"));
  return user;
 }
}

22.Spring回滚的情况:
runtimeException--------------------回滚
public void delete(Integer id) {
  this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id},
    new int[]{java.sql.Types.INTEGER});
  throw new RuntimeException();
 }不会执行删除操作。
checkedException--------------------不回滚
public void delete(Integer id) throws Exception {
  this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id},
    new int[]{java.sql.Types.INTEGER});
  //throw new RuntimeException();
 }
@Test
 public void deleteUser() {
  try {
   us.delete(2);
  } catch (Exception e) {
   e.printStackTrace();
  }
  //上面是对checkedException回滚的测试
  //us.delete(1);
 }//不回滚
这些默认管理行为可以修改:
@Transactional(RollbackFor=Exception.class)
 public void delete(Integer id) throws Exception {
  this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id},
    new int[]{java.sql.Types.INTEGER});
  //throw new RuntimeException();
 }
在delete方法上指定checkedException也可以回滚
@Transactional(noRollbackFor=RuntimeException.class)
public void delete(Integer id)  {
this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id},
new int[]{java.sql.Types.INTEGER});
throw new RuntimeException();
}
指定delete方法上RuntimeException不回滚。
指定有些方法不需要开启事务管理(只有增删改才需要):
@Transactional(propagation=Propagation.NOT_SUPPORTED)
 public UserBean getUser(Integer id) {
  UserBean user = new UserBean();
  user = (UserBean)this.jdbcTemplate.queryForObject("select * from T_Spring where id=?", new Object[]{id},new UserRowMapper());
  return user;
 }
事务传播属性:
REQUIRED:该方法需要一个事务,如果方法执行时已经在一个事务当中则加入该事务,够则创建一个新事务,默认属性!
NOT_SUPPORTED:不需要事务。当这个方法在其他对象的需要事务支持的方法中执行的时候,原先的事务就会被挂起,等这个不需要事务的方法执行结束后才恢复执行。
REQUIRESNES:不论当前是否有事务开启,该方法都需要开启一个新事务,并且会让其他事务挂起,等方法执行完,新事务结束后原先事务才恢复执行。
MANDATORY:要求方法必须在一个已有的事务中执行,方法本身不能开启事务,否则会出错。
SUPPORTS:可以在事务中执行,也可以不在事务中执行。
NEVER:绝对不能在事务中执行。
NESTED:回滚至savepoint。
其他属性设置:
readOnly:boolean类型,是否为只读事务。
isolation:事务隔离级别:(MYSQL默认第三种)
isolation=Isolation.REPEATABLE_READ可重复读,有幻读(有记录插入和删除的操作)。
isolation=Isolation.SERIALIZABLE串行化,事务无并发,最高级别。


23.基于xml方式配置事务管理:

            
         //定义个切入点,对所有需要事务管理的方法进行拦截,同时把这些拦截到的方法教给一个通知处理器txAdvice处理 
      
       
       
    
    
     //定义个通知处理器,它的事务管理器为txManager 
      
       
        
        
      
 
    
     
已经配置好了,假如事务管理器能够管理事务,则在下面有问题的delete方法执行的时候,两个操作都不会提交(否则下面方法中的两个语句的执行是在两个事务中执行的): 
//@Transactional(noRollbackFor=Exception.class) 
 public void delete(Integer id) throws Exception { 
  this.jdbcTemplate.update("delete from T_Spring where id=?", new Object[]{id}, 
    new int[]{java.sql.Types.INTEGER}); 
  this.jdbcTemplate.update("delete from T_ Springss where id=6"); 
  //throw new RuntimeException(); 
 } 
结果两个删除都没有执行,把配置文件注释掉后,前面语句已经提交,后面的抛出异常。