spring4学习笔记

时间:2023-03-08 16:17:52

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