点石成金什么意思: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配置:
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
Private List
Set集合类型注入:
List集合类型注入:
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: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: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: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自动管理
//注册事务管理的处理器
//${}读取配置文件参数,为属性注入值
@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
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
List
users = (List
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();
}
结果两个删除都没有执行,把配置文件注释掉后,前面语句已经提交,后面的抛出异常。