IOC
public class IServiceImpl implements IService {
public void IserviceImpl(){}
@Override
public void getService() {
System.out.println("服务");
}
}
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.xsd"> <bean id="myService" class="com.pack.Service.IServiceImpl"/> </beans>
ApplicationContext来加载类
通过ClassPathXmlApplicationContext来加载类
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
IService service = (IService) ac.getBean("myService");
service.getService();
通过FileSystemXmlApplicationContext加载
ApplicationContext ac = new FileSystemXmlApplicationContext();
IService service = (IService) ac.getBean("H:\\Program\\WorkSpace\\Java\\SpringLearn\\src\\applicationContext.xml");
service.getService();
BeanFactory
已废用
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
IService service = (IService) bf.getBean("myService");
service.getService();
两个容器的区别
ApplicationContext 在容器初始化时,会将所有的bean创建
优点:响应快
缺点:占用资源
BeanFactory,在真正使用bean时才创建
优点:不多占用系统资源
缺点:响应慢
相较于资源占用,我们一般选择响应速度快的ApplicationContext
动态工厂
ServiceFactory
public class ServiceFactory {
public IService getService(){
return new IServiceImpl();
}
}
<bean id="myService" factory-bean="factory" factory-method="getService"/>
<bean id="factory" class="com.pack.Service.ServiceFactory"/>
String configLocation = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(configLocation);
IService service = (IService) ac.getBean("myService");
service.getService();
静态工厂
<bean id="myService" class="com.pack.Service.ServiceFactory" factory-method="getService"/>
ServiceFactory
public class ServiceFactory {
public static IService getService(){
return new IServiceImpl();
}
}
Bean创建
<bean id="myService" class="com.pack.Service.ServiceFactory" factory-method="getService" scope="prototype"/>
scope 为bean的管理有singleton prototype request session
其对象的创建时机不是在初始化时,而是在访问时
singleton是默认方式,创建在初始化时。
request 对于一个Http请求,创建一次
session 对于每一个Http-Session, 创建一次
Bean的生命周期
public class IServiceImpl implements IService, BeanNameAware, BeanFactoryAware, InitializingBean, DisposableBean {
private String adao; public IServiceImpl(String adao) {
System.out.println("step1: setter()");
} public void setAdao(String adao) {
this.adao = adao;
System.out.println("step2: setter()");
} @Override
public String getService() {
System.out.println("Step9: 服务");return "abcde";
} public IServiceImpl() {
} @Override
public void setUp() {
System.out.println("Step7: bean初始化之后");
} @Override
public void tearDown() {
System.out.println("Step11: 终止");
} @Override
public void setBeanName(String name) {
System.out.println("Step3: BeanNameAware 获取beanName" +name);
} @Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware Step4: 获取beanFactory");
} @Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean Step 6: bean初始化完毕");
} @Override
public void destroy() throws Exception {
System.out.println("InitializingBean Step 10: 实现接口的销毁之前"); }
}
public class BeanLife implements BeanPostProcessor {
public BeanLife() {
System.out.println("构造Bean"); } @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("step5: 执行Bean前处理");
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Step8: 执行Bean后处理");
return bean;
}
}
Step1: 构造器
step2: setter()
Step3: BeanNameAware 获取beanNamemyService
BeanFactoryAware Step4: 获取beanFactory
step5: 执行Bean前处理
InitializingBean Step 6: bean初始化完毕
Step7: bean初始化之后
Step8: 执行Bean后处理
Step9: 服务
InitializingBean Step 10: 实现接口的销毁之前
Step11: 终止
Bean后处理器的应用
public class BeanLife implements BeanPostProcessor {
public BeanLife() {
System.out.println("构造Bean"); } @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean前处理");
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean后处理");
Object obj = Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(bean, args);
return ((String)object).toUpperCase();
}
}
);
return obj;
}
}
基于XML的设值注入
public class User {
private String name;
private Integer role;
private Country country; @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", role=" + role +
", country=" + country +
'}';
} public void setName(String name) {this.name = name;} public void setRole(Integer role) {this.role = role;} public void setCountry(Country country) {this.country = country;}
}
要实现setter方法, Country实现 省略
<bean id="china" class="com.pack.beans.Country">
<property name="name" value="中国"/>
</bean>
<bean id="user" class="com.pack.beans.User">
<property name="name" value="李四"/>
<property name="role" value="1"/>
<property name="country" ref="china"/>
</bean>
构造注入
public class User {
private String name;
private Integer role;
private Country country; public User() {
} public User(String name, Integer role, Country country) {
this.name = name;
this.role = role;
this.country = country;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", role=" + role +
", country=" + country +
'}';
}
}
必须实现带参构造器
<bean id="user" class="com.pack.beans.User">
<constructor-arg name="name" value="李四"/>
<constructor-arg index="1" value="1"/>
<constructor-arg ref="china"/>
</bean>
可以不写index 但是顺序必须和带参构造器的顺序一样
p命名空间
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="china" class="com.pack.beans.Country">
<property name="name" value="中国"/>
</bean>
<bean id="user" class="com.pack.beans.User" p:name="李四" p:role="2" p:country-ref="china"/>
</beans>
c命名空间
xmlns:c="http://www.springframework.org/schema/c"
<bean id="user" class="com.pack.beans.User" c:name="李四" c:role="2" c:country-ref="china"/>
为集合属性注入
<bean id="china" class="com.pack.beans.Country">
<property name="name" value="中国"/>
</bean>
<bean id="user" class="com.pack.beans.User">
<property name="name" value="李四"/>
<property name="role">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
</list>
</property>
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<property name="country" ref="china"/>
</bean>
简单写法
<bean id="user" class="com.pack.beans.User">
<property name="name" value="李四"/>
<property name="role" value="1,2"/>
<property name="myList" value="list1, list2"/>
<property name="mySet" value="set1, set2"/>
<property name="country" ref="china"/>
</bean>
域属性自动注入
<bean id="id" class="class" autowire="xxx">
...
</bean>
byType去容器中找与属性 类相同的bean来填充。bean必须唯一
byName 找与属性名相同的bean来填充
SPEL注入
<bean id="user" class="com.pack.beans.User">
<property name="name" value="张三"/>
<property name="role" value="#{T(java.lang.Math).random() * 10}"/>
</bean>
<bean id="liming" class="com.pack.beans.User">
<property name="name" value="李四"/>
<property name="role" value="#{user.role}"/>
</bean>
调用方法
public Integer changeRole(){
if (name.equals("张三")){
return 12;
}
return role;
}
<bean id="liming" class="com.pack.beans.User">
<property name="name" value="李四"/>
<property name="role" value="#{user.changeRole()}"/>
</bean>
要实现getter方法
多个配置文件
平行配置
有两个配置文件 spring-1.xml 和 spring-2.xml
String resource = "spring-01.xml";
String resource2 = "spring-02.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource, resource2);
或者
String resource = "spring-01.xml";
String resource2 = "spring-02.xml";
String[] resources = {resource, resource2};
ApplicationContext ac = new ClassPathXmlApplicationContext(resources);
或者通配符模式
String resource = "spring-*.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
包含配置
<import resource="spring-user01.xml"/>
或者通配符模式
<import resource="spring-*.xml"/>
但是 主配置与子配置命名格式不能一样
基于注解的DI
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"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- 扫描这个包及其子包-->
<context:component-scan base-package="com.anno.bean"/>
<!-- 扫描这个包的子包-->
<context:component-scan base-package="com.anno.*"/>
</beans>
@Scope("prototype") //默认
@Component("myUser")
public class User {
@Value("李四")
private String name;
@Value(value = "12")
private Integer age;
private Country country; ...
}
与@Component功能相同,但意义不同的注解还有三个
1)@Repository: 注解在Dao实现类
2)@Service: 注解在Service实现类
3)@Controller: 注解在SpringMVC的处理器上
AOP
Advice
前置通知
public class LogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("log output");
}
}
后置通知
可以获得方法的返回值,但无法改变返回结果
public class LogAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("After Log");
}
环绕通知
public class MMethodIntercept implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before");
Object result = invocation.proceed();
System.out.println("after");
return result;
}
}
异常通知
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){ //这里可以指定异常,当发生指定异常时,执行指定的方法
// 方法名固定
System.out.println("exception occur");
}
}
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org//schema/spring-aop.xsd"> <bean id="myService" class="com.AOPLearn.Service.IServiceImpl"/> <bean id="myAdvice" class="com.AOPLearn.Proxy.MyThrowsAdvice"/>
<bean id="myAdvice2" class="com.AOPLearn.Proxy.LogAfterAdvice"/>
<bean id="myAdvice3" class="com.AOPLearn.Proxy.LogBeforeAdvice"/>
<bean id="myAdvice4" class="com.AOPLearn.Proxy.MMethodIntercept"/> <bean id="enhencedService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="myService"/>
<property name="interceptorNames">
<array>
<value>myAdvice</value>
<value>myAdvice2</value>
<value>myAdvice3</value>
<value>myAdvice4</value>
</array>
</property>
<!-- <property name="target" ref="myService"/>-->
<!-- <property name="interfaces" value="com.AOPLearn.Proxy.LogAdvice"/>-->
</bean> </beans>
IService service = (IService) ac.getBean("enhencedService");
service.doFirst();
此时service是jdk proxy, 想使用cglib proxy,只需把service,interface去掉,不使用接口便默认使用cglib
有接口默认使用jdk proxy,无接口默认使用cglib proxy
有借口时指定cglib的只需加入如下语句
<property name="proxyTargetClass" value="true"/>
或者
<property name="optimize" value="true"/>
Advisor
名称匹配方法切入
<bean id="myAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myAdvice"/>
<property name="mappedName" value="doFirst"/> //可以使用通配符
</bean>
正则匹配方法切入
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="myAdvice"/>
<property name="pattern" value=".*doFirst"/> //正则表达式匹配的是全限定方法名
</bean>
默认自动代理生成器
由于 主业务(service)与(serviceProxy)存在耦合,还需解耦。
且 对于多个service 每个service方法都要注册太冗余。需要使用自动代理生成器。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> //源码分析后:xxCreator实现了bean后处理器,使得bean被初始化后,被enhence了
缺陷:
1)不能选择目标对象。
2)不能选择切面类型,切面只能是advisor。
3)不能选择advisor,所有advisor都将被织入目标方法。
名称自动代理生成器
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="myService"/>
<property name="interceptorNames" value="myAdvisor2"/>
</bean>
AspectJ Aop
<bean id="myAspect" class="com.AOPLearn.Proxy.MyAjProxy"/>
<bean id="MyService" class="com.AOPLearn.AJ.BServiceImpl"/>
<aop:aspectj-autoproxy/>
XML式
<aop:config>
<aop:aspect id="myAspect">
<aop:pointcut id="firstPC" expression="execution(* *..BService.doFirst(..))"/>
<aop:before method="myBefore" pointcut-ref="firstPC"/>
<aop:after-returning method="myAfterReturning" pointcut="execution(* *..BService.doSecond(..))"/>
<aop:around method="myAround" pointcut-ref="firstPC"/>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="firstPC"/>
<aop:after method="myAfter" pointcut="execution(* *..BService.doFirst(..))"/>
</aop:aspect>
</aop:config>
注解式
@Aspect
public class MyAjProxy {
@Before(value = "execution(* *..BService.doFirst(..))")
public void before(JoinPoint jp){
System.out.println("before method jp " + jp);
}
@AfterReturning(value = "execution(* *..BService.doSecond(..))", returning = "result")
public void myAfterReturning(Object result){
System.out.println("after method result " +result);
}
@Around(value = "execution(* *..BService.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before");
Object obj = pjp.proceed();
System.out.println("before");
return obj;
}
@AfterThrowing(value = "execution(* *..BService.doFirst(..))")
public void myAfterThrowing(){
System.out.println("exception occur");
}
@After(value = "doFirstPointcut()")
public void myAfter(){
System.out.println("final occur");
}
//指定 point点
@Pointcut("execution(* *..BService.doFirst(..))")
public void doFirstPointcut(){}
}
应用
DAO
spring中读取配置信息
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="jdbc.properties"/>
</bean>
注册数据源
<bean id="dataSourceJDBC" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="baobao521"/>
</bean> <bean id="dataSourceDBCP" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/java?serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="baobao521"/>
</bean> <bean id="dataSourceC3P0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java?serverTimezone=GMT%2B8"/>
<property name="user" value="root"/>
<property name="password" value="baobao521"/>
</bean>
注册JdbcTemplate
<bean id="jdbcTemplate1" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceJDBC"/>
</bean>
可以不注册,保留一下或许有用
注册dao
<bean id="userDao" class="com.SDAO.DAO.IUserDaoImpl">
<property name="dataSource" ref="dataSourceJDBC"/>
</bean>
实现dao层
public class IUserDaoImpl extends JdbcDaoSupport implements IUserDao {
@Override
public void insertUser(User user) {
String sql = "insert into user(username, password, role) values(?,?,?)";
this.getJdbcTemplate().update(sql, user.getUsername(),user.getPassword(), user.getRole());
} @Override
public void deleteById(Integer id) {
String sql = "delete from user where id=?";
this.getJdbcTemplate().update(sql, id);
} @Override
public void updateUser(User user) {
String sql = "update user set password=?,role=? where id=?";
this.getJdbcTemplate().update(sql, user.getPassword(), user.getRole(), user.getId());
} @Override
public List<User> selectAll() {
String sql = "select * from user";
return this.getJdbcTemplate().query(sql, new UserRowMapper());
} @Override
public User selectById(Integer id) {
String sql = "select * from user where id=?";
return (User) this.getJdbcTemplate().queryForObject(sql, new UserRowMapper(), id);
}
}
不能简单的将JdbcTemplate抽取出来
JdbcTemplate是多实例的,即系统会为每一个模板线程创建一个JdbcTemplate实例,并在该线程结束时,自动释放该实例。所以每次调用该实例时,都要通过getTemplate方法。
自定义类型查找操作需要RowMapper
public class UserRowMapper implements RowMapper {
@Override
public Object mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setRole(resultSet.getInt("role"));
return user;
}
}
Spring事务管理
PlatformTransactionManager接口两个实现类
DataSourceTransactionManager 使用Jdbc 或 ibatis 进行持久化存储时使用
HibernateTransactionManager 使用Hibernate时使用
5个事务隔离级别 //todo
7个事务传播行为
REQUIRED 指定方法必须在事务下运行,没有事务则创建事务
SUPPORTS 可以在事务下运行,也可以在非事务下运行
MANDATORY 必须在事务下执行, 没事务直接抛出异常
REQUIRED_NEW 总是新建一个事务来执行方法
NOT_SUPPORTED 指定方法不能在事务环境下执行,当前存在事务则将事务挂起
NEVER 指定方法不能在事务环境下执行,当前存在事务则将抛出异常
NESTED 指定方法必须在事务下运行,有则在嵌套事务内运行,没有则创建事务运行???
Spring事务的默认回滚方式 发生运行时异常回滚,发生受查异常提交。
XML式
注册事务管理bean
<bean id="myTM" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceJDBC"/>
</bean>
<bean id="serviceProxy1" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="myTM"/>
<property name="target" ref="service1"/>
<property name="transactionAttributes">
<props>
<prop key="SB">-SBException</prop> //key 指定方法 -xxx 指定异常,受查异常默认提交
</props>
</property>
</bean>
AspectJ方式
<tx:advice id="txAdvice" transaction-manager="myTM">
<tx:attributes>
<tx:method name="SB" isolation="DEFAULT" propagation="REQUIRED" rollback-for="SBException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myPC1" expression="execution(* *..Service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPC1"/>
</aop:config>
注解式
xmlns:tx="http://www.springframework.org/schema/tx"
...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
...
<tx:annotation-driven transaction-manager="myTM"/>
要添加事务的方法上加上以下语句
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED, rollbackFor = xxxException.class)
SpringWeb
applicationContext的注册,在监听器中,可以实现在tomcat容器初始化时,使applicationContext单实例化。SpringWeb帮我们做好了这些
只需在web.xml中注册
<listenner>
<listenner-class>org.springframework.web.context.ContextLoaderListenner</listenner-class>
</listenner>
制定配置文件的位置,默认情况下只能放在web-inf下
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:xxx.xml</param-value>
</context-param>
获取Spring容器对象
WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext);
//todo