一、准备工作:
说明:本spring实例基于spring2.5.6。
1.新建实体类com.hanjun.entity.User。有以下属性及getter、setter方法:
private String username;
private String password;
2.新建DAO接口com.hanjun.dao.UserDao。声明方法:
public void save(User user);
3.新建DAO接口实现类com.hanjun.dao.impl.UserDaoImpl。实现方法:
public void save(User user) {
System.out.println("user saved!");
}
4.新建service类com.hanjun.service.UserService。声明UserDao属性及getter、setter和add方法:
private UserDAO userDAO;
public void add(User user) {
userDAO.save(user);
}
二、模拟spring注入
1.src下新建beans.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="u" class="com.hanjun.dao.impl.UserDAOImpl" />
<bean id="userService" class="com.hanjun.service.UserService" >
<property name="userDAO" bean="u"/>
</bean>
</beans>
2.新建interface com.hanjun.spring.BeanFactory,声明方法:
public Object getBean(String id);
3.新建class com.hanjun.spring.ClassPathXmlApplicationContext 实现com.hanjun.spring.BeanFactory:
//用于存放xml中配置的bean
private Map<String, Object> beans = new HashMap<String, Object>();
//初使化时解析xml中配置的bean,存入beans
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml")); // 构造文档对象
Element root = doc.getRootElement(); // 获取根元素
List list = root.getChildren("bean");// 取名字为bean的所有元素
for (int i = 0; i < list.size(); i++) {
Element element = (Element) list.get(i);
String id = element.getAttributeValue("id"); //获取bean的id属性值
String clazz = element.getAttributeValue("class");//获class属性值
Object o = Class.forName(clazz).newInstance();
beans.put(id, o);
for (Element propertyElement : (List<Element>) element.getChildren("property")) {
String name = propertyElement.getAttributeValue("name"); // userDAO
String bean = propertyElement.getAttributeValue("bean"); // u
Object beanObject = beans.get(bean);// UserDAOImpl instance
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
Method m = o.getClass().getMethod(methodName,beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
}
}
}
public Object getBean(String id) {
return beans.get(id);
}
4.测试:
BeanFactory applicationContext = new ClassPathXmlApplicationContext();
UserService service = (UserService)applicationContext.getBean("userService");
User u = new User();
u.setUsername("zhangsan");
u.setPassword("zhangsan");
service.add(u);
5.说明:此例有bug,因为加载bean时为顺序从上往下,所以被注入的bean需配置在上面。解析xml需要jdom.jar。
三、注入(xml)
1.引入spring.jar和commons-logging.jar
2.src下新建applicationContext.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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">
<bean id="u" class="com.hanjun.dao.impl.UserDAOImpl"></bean>
<bean id="userService" class="com.hanjun.service.UserService">
<property name="userDAO" ref="u" />
</bean>
</beans>
3.测试代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService)ctx.getBean("userService");
User u = new User();
u.setUsername("zhangsan");
u.setPassword("zhangsan");
service.add(u);
4.我们常用的就是setter注入方式,spring还支持构造方法注入方式:
<bean id="userService" class="com.hanjun.service.UserService">
<constructor-arg ref="u"></constructor-arg>
</bean>
spring将id为u的bean做为参数调用UserService类对应的构造方法。如果参数不止一个,则可以通过参数类型或索引区分:
<constructor-arg type="int" value="1"></constructor-arg>
<constructor-arg type="java.lang.String" value="str"></constructor-arg>
或
<constructor-arg index="0" value="1"></constructor-arg>
<constructor-arg index="1" value="str"></constructor-arg>
5.简单类型注入和集合类型注入,为简单类型变量注入值:
<property name="score" value="99" />
为Set类型变量注入值:
<property name="sets">
<set>
<value>1</value>
<value>2</value>
</set>
</property>
为List类型变量注入值:
<property name="lists">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
为Map类型变量注入值:
<property name="maps">
<map>
<entry key="1" value="1"></entry>
<entry key="2" value="2"></entry>
<entry key="3" value="3"></entry>
<entry key="4" value="4"></entry>
</map>
</property>
为Properties类型变量注入值:
<property name="propertys">
<props>
<prop key="1">1</prop>
<prop key="2">2</prop>
<prop key="3">3</prop>
<prop key="4">4</prop>
</props>
</property>
四、注入(annotation)
1.applicationContext.xml中,beans标签如下,蓝色部分为新增。
<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"
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">
2.beans中增加配置如下:
<context:annotation-config />
3.在要注入的setter方法上注解@Autowired,则spring会根据参数类型找到对应类型的bean为参数,如果对应类型的bean大于1个,则会出错。解决办法为在参数类型前注解@Qualifier("bean的id或name")指定要注入哪个bean。
@Autowired
public void setUserDAO(@Qualifier("u") UserDAO userDAO) {
this.userDAO = userDAO;
}
4.相对于@Autowired,还有另一种注解方式。在要注入的setter方法上注解@Resource,spring会首先根据setter方法的名字找对应的bean做为参数,如果找不到则再根据参数的类型找对应类型的bean做为参数。@Resource(name="bean的id或name")则可以指定注入哪个bean。@Resource所用的是javax.annotation.Resource类,此类在jdk1.6引入,所以当使用jdk1.6以下版本时,想用@Resource,需引入common-annotations.jar。
5.bean的注解方式:applicationContext.xml的beans中增加配置:
<context:component-scan base-package="com.hanjun"/>
然后在需要配置class类上面注解@Component,则spring初使化时会扫描指定包下的所有类,对有此注解的类自动初使化为spring的bean类,name为类名(第一个字母小写)。@Component("bean的name")则可指定bean的name。
附录,已有bean的实例如何获取bean的id
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
IUserDao userDao = ctx.getBean(IUserDao.class);
for(String beanId :ctx.getBeanNamesForType(IUserDao.class)){
if(ctx.getBean(beanId)==userDao){
System.out.println(beanId);
}
}
五、bean的属性
1.scope:初使化方式。默认值为singleton,即每次获取的bean为同一个。当值为prototype时,每次获取都new一个新的类。对于struts的action类,常设置为prototype。
annotation方式:class上注解@Scope("prototype")。
2.autowire:自动注入。默认值为no,即不自动注入。当值为byType时,spring会根据参数类型找对应类型的bean做为参数,如果对应类型的bean多于1个,则会出错。当值为byName时,spring会根据setter方法的名字找对应id或name的bean做为参数,例如根据setStu方法找id或name为stu的bean做为参数。
annotation方式:见spring注入(annotation)。
3.lazy-init:懒加载。默认值为false,即在spring初使化时就初使化此bean。当值为true时,则当使用到此bean时才初使化。
4.init-method="方法名":bean初使化时运行此方法。
annotation方式:在方法上注解@PostConstruct。
5.destroy-method="方法名":bean销毁时运行此方法。
annotation方式:在方法上注解@PreDestroy。
六、动态代理(jdk)
要求:在执行UserDaoImpl的所有方法前,打印"方法名 strat"。
1.新建class com.hanjun.aop.LogInterceptor。实现java.lang.reflect.InvocationHandler接口:
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public void beforeMethod(Method m) {
System.out.println(m.getName() + " start");
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
beforeMethod(m);
m.invoke(target, args);
return null;
}
2.测试代码:
UserDAO userDAO = new UserDAOImpl();
LogInterceptor li = new LogInterceptor();
li.setTarget(userDAO);
UserDAO userDAOProxy = (UserDAO)Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), userDAO.getClass().getInterfaces(), li);
userDAOProxy.save(new User());
3.这样,就由我们写的LogInterceptor类来代理执行UserDaoImpl类的方法,可以在invok方法内任意增加代码。需要注意的是,被代理的方法必须是实现接口的方法。
七、aop(xml)
1.applicationContext.xml中,beans标签如下,蓝色部分为新增。如果被代理的方法不是实现接口的方法,还需要导入cglib-nodep-2.1_3.jar。
<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-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/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
2.新建com.hanjun.aop.LogInterceptor类:
public class LogInterceptor {
public void before() {
System.out.println("method before");
}
}
3.beans中增加配置:
<bean id="logInterceptor" class="com.hanjun.aop.LogInterceptor"></bean>
<aop:config>
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut="execution(public void com.hanjun.service.UserService.add(com.bjsxt.model.User))" />
</aop:aspect>
</aop:config>
4.测试代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService)ctx.getBean("userService");
service.add(new User());
5.打印结果:
method before
user saved!
6.说明:spring的aop类需要配置为bean。aop的配置写在aop:config内。aop:aspect的ref属性指定对哪个aop类进行配置。aop:before标签的methode属性指定的方法会在pointcut织入点语句指定的对象执行前执行。
7.织入点语法:
execution(* sa* (..)) 任何返回值,任何包下的以sa为命名开始的方法;
execution(* com.hanjun.dao.impl.UserDAOImpl.*(..)) 指定类的任何方法;
execution(* com.hanjun.dao.impl.*.*(..)) 指定包下的任何类的任何方法;
execution(* com.hanjun.dao..*.*(..)) 指定包下的任何子包下的任何类的任何方法;
8.其它advice:
<aop:after-returning method="方法名" pointcut="织入点语句"/> 指定对象执行后执行指定方法
<aop:after-throwing method="方法名" pointcut="织入点语句"/> 指定对象执行出现异常时执行指定方法
9.如果多个aop的配置用同样的织入点语句,则可以在aop:config配置aop:pointcut,expression属性指定织入点语句,id为引用标识。aop:before的pointcut-ref等于aop:pointcut的id:
<aop:config>
<aop:pointcut expression="execution(* com.bjsxt.service.*.*(..))" id="servicePointuct"/>
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut-ref="servicePointuct" />
</aop:aspect>
</aop:config>
10.环绕拦截:在com.hanjun.aop.LogInterceptor中增加方法:
public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("method around start");
pjp.proceed();
System.out.println("method around end");
}
在aop:config标签中增加配置:
<aop:around method="aroundMethod" pointcut="execution(* com.bjsxt.service.*.*(..))"/>
11.说明:如果被代理的方法为实现接口的方法,spring会用jdk的动态代理方式。如果不是实现接口的方法,需要导入cglib-nodep-2.1_3.jar,则spring会直接操作二进制码来实现代理。
八、aop(annotation)
1.引入aspectjrt.jar和aspectjweaver.jar。
2.applicationContext.xml中,beans标签如下,蓝色部分为新增。
<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-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/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
3.beans中增加配置:
<aop:aspectj-autoproxy />
4.新建com.hanjun.aop.LogInterceptor类:
@Aspect
@Component
public class LogInterceptor {
@Before("execution(public void com.hanjun.dao.impl.UserDAOImpl.save(com.hanjun.entity.User))")
public void before() {
System.out.println("method before");
}
}
5.测试代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService)ctx.getBean("userService");
service.add(new User());
6.打印结果:
method before
user saved!
7.说明:在类上注解了@Aspect的类为spring的aop类;aop类本身也要spring管理,所以在类上注解@Component;在aop类的方法上注解@Before("织入点语句")则会在织入点语法指定对象运行前执行此方法。
8.织入点语法:
execution(* sa* (..)) 任何返回值,任何包下的以sa为命名开始的方法;
execution(* com.hanjun.dao.impl.UserDAOImpl.*(..)) 指定类的任何方法;
execution(* com.hanjun.dao.impl.*.*(..)) 指定包下的任何类的任何方法;
execution(* com.hanjun.dao..*.*(..)) 指定包下的任何子包下的任何类的任何方法;
9.其它advice:
@AfterReturning("织入点语句") 在织入点语句指定的对象执行完之后执行;
@AfterThrowing("织入点语句") 在织入点语句指定的对象执行出现异常时执行;
10.如果多个方法用的织入点语句相同,则可以用给一个空方法注解@Pointcut("织入点语句"),下面可以用这个方法名来共用织入点语句:
@Pointcut("execution(* com.hanjun.dao..*.*(..))")
public void myMethod(){}
@AfterReturning("myMethod()")
public void after() {
System.out.println("method after");
}
11.环绕拦截:
@Around("execution(* com.bjsxt.dao..*.*(..)))")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around start");
pjp.proceed();
System.out.println("around end");
}
12.说明:如果被代理的方法为实现接口的方法,spring会用jdk的动态代理方式。如果不是实现接口的方法,需要导入cglib-nodep-2.1_3.jar,则spring会直接操作二进制码来实现代理。
九、datasource注入
在mysql数据库新建test数据库,新建表t_user,列userid、username、password。
1.在applicationContext.xml的beans标签中增加配置:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
2.引入mysql-connector-java-5.1.13-bin.jar和spring的commons-dbcp.jar、commons-pool.jar。
3.在com.hanjun.dao.impl.UserDAOImpl类中新增DataSource属性和修改save方法:
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
@Resource
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void save(User user) {
try {
String sql="insert into user values(null,'zhangsan','123456')";
Connection conn=dataSource.getConnection();
conn.createStatement().executeUpdate(sql);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
4.这样,就可以通过spring注入的dataSource来获取Connection。
5.数据库的属性可以另外配置,src下新建jdbc.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
applicationContext.xml的beans标签中增加配置:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:jdbc.properties</value>
</property>
</bean>
修改dataSource的配置:
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
十、sessionFactory注入
1.引入hibernate的jar包。
2.在com.hanjun.entity包下新建User.hbm.xml,对User类进行hibername的实体类配置。
3.在spring的applicationContext.xml的beans标签中,增加sessionFactory的配置,将spring管理的dataSource注入进来:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>com/hanjun/entity/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
mappingResources属性指定hibernate实体类的配置文件列表。hibernateProperties属性指定hibernate的属性配置。如果hibernate用annotation配置实体类,则sessionFactory的class改为org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean,属性mappingResources改为annotatedClasses,list的值改为类列表,如com.hanjun.entity.User。或用packagesToScan属性让spring去扫描哪些是hibernate实体类:
<property name="packagesToScan">
<list>
<value>com.hanjun.entity</value>
</list>
</property>
4.修改com.hanjun.dao.impl.UserDAOImpl类:
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Resource
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public void save(User user) {
Session s = sessionFactory.openSession();
s.beginTransaction();
s.save(user);
s.getTransaction().commit();
s.close();
}
5.测试代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = (UserService)ctx.getBean("userService");
System.out.println(service.getClass());
User u=new User();
u.setUsername("zhangsan");
u.setPassword("123456");
service.add(u);
十一、管理hibernate事务
1.在applicationContext.xml中,beans标签如下,蓝色部分为新增。
<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"
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/context
http://www.springframework.org/schema/context/spring-context-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/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
2.在beans中增加配置:
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
如果用annotation配置事务,还需要增加配置:
<tx:annotation-driven transaction-manager="txManager"/>
3.修改com.hanjun.dao.impl.UserDAOImpl的save方法,sessionFactory.openSession()改为sessionFactory.getCurrentSession(),不需要打开和提交事务:
public void save(User user) {
Session s = sessionFactory.getCurrentSession();
s.save(user);
}
4.在com.hanjun.service.UserService的add方法上注解@Transactional:
@Transactional
public void add(User user) {
userDAO.save(user);
Log log = new Log();
log.setMsg("a user saved!");
logDAO.save(log);
}
5.说明:新增的Log类和logDAO类,在操作用户后记录日志,不详述。如此,如果在记录日志时出现异常,则新加的用户会回滚。
6.transaction的属性:
propagation : @Transactional(propagation=Propagation.REQUIRED),默认为Propagation.REQUIRED,代表如果已经有transaction,则会用这个transaction,如果没有,则会新建一个transaction。当值为Propagation.MANDATORY时,代表必须已经有transaction,如果没有会报错。当值为Propagation.REQUIRES_NEW时,代表这个方法用独立内嵌的transaction,如果回滚不会影响外面的transaction。当值为Propagation.NEVER时,代表外面必须不能有transaction,如果有则会报错。
readOnly : @Transactional(readOnly=true),默认为false,如果为true,代表本事务内没有插入、修改、删除的操作,如果有则报错。此属性主要用于优化。
timeout : @Transactional(timeout=60),如果这个transaction执行超过60秒,则终止掉。
7.xml方法:
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="getUser" read-only="true" />
<tx:method name="add*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="bussinessService"
expression="execution(public * com.hanjun.service..*.*(..))" />
<aop:advisor pointcut-ref="bussinessService"
advice-ref="txAdvice" />
</aop:config>
十二、hibernateTemplate注入
1.applicationContext.xml的beans中增加配置:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
2.修改com.hanjun.dao.impl.UserDAOImpl:
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
@Resource
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public void save(User user) {
hibernateTemplate.save(user);
}
3.这样,在执行数据库操作时,直接操作就可以了,其它的不需要考虑。
4.org.springframework.orm.hibernate3.support.HibernateDaoSupport可以管理HibernateTemplate:
新建com.hanjun.dao.SuperDAO:
@Component
public class SuperDAO extends HibernateDaoSupport {
@Resource(name="sessionFactory")
public void setSuperSessionFactory(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
}
则com.hanjun.dao.impl.UserDAOImpl不再需要hibernateTemplate属性,直接写方式即可:
@Component("userDAO")
public class UserDAOImpl extends SuperDAO implements UserDAO {
public void save(User user) {
this.getHibernateTemplate().save(user);
}
}