Spring 特点
1. 提供了IoC容器,方便解耦,简化开发;
2. 支持AOP,面向切面编程;
3. 支持声明式事务;
4. 方便程序的测试;
5. 方便集成其它框架;
Spring 组织架构
Spring 核心模块
1. spring-core:提供了框架的基本组成部分,包括IoC和DI功能;
2. spring-beans:提供BeanFactory;
3. spring-context:spring的上下文,即IoC容器;
4. spring-context-support:提供对第三方集成到spring上下文的支持;
5. spring-expression:spring表达式语言;
1、Spring IoC
IoC,inverse of control 控制反转。即把原来需要通过 new 关键字创建的对象交由 IoC 容器来管理。
1.1、两种容器
BeanFactory 和 ApplicationContext;
ApplicationContext是BeanFactory的子接口,实现了BeanFactory的所有功能,还扩展了其它功能,是一种高级的容器,一般建议使用ApplicationContext。
1.2、ApplicationContext 接口实现
1. ClassPathXmlApplicationContext***:该容器从XML文件中加载已被定义的bean。不需要提供XML文件的完整路径,会从CLASSPATH中搜索bean配置文件;
2. FileSystemXmlApplicationContext:需要提供XML文件的完整路径;
1.3、基于XML配置文件的实现过程
1.3.1、添加依赖
<dependencies>
<!--spring的核心工具包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!--spring IoC的基础实现,包含访问配置文件、创建和管理bean等-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!--在基础IoC功能上提供扩展服务,邮件服务、任务调度、远程访问等-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!--spring context的扩展支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!--spring表达式语言-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!--测试工具-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
1.3.2、创建配置文件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="对象名" class="类的完整路径">
<property name="属性名" ref="被引用的对象id"></property>
</bean>
</beans>
1.3.3、加载配置文件,获取对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Object obj = context.getBean("对象id");
1.4、bean标签的属性
1.5、对象创建的方式
1.5.1 无参构造
public StudentDao() {
System.out.println("使用了无参构造");
}
<bean id="studentDao" class="com.zhy.dao.StudentDao"></bean>
1.5.2 有参构造
public StudentDao(String name, int age) {
System.out.println("使用了有参构造:"+name+","+age);
this.name = name;
this.age = age;
}
<!--有参构造-->
<bean id="studentDao1" class="com.zhy.dao.StudentDao">
<constructor-arg name="name" value="周二宇"></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
</bean>
1.5.3 静态方法
public static void static_method(){
System.out.println("调用了静态方法");
}
<bean id="studentDao2" class="com.zhy.dao.StudentDao" factory-method="static_method"></bean>
1.5.4 非静态方法
public void no_static_method(){
System.out.println("调用了非静态方法");
}
<bean id="studentDao" class="com.zhy.dao.StudentDao"></bean>
<bean id="studentDao4" factory-method="no_static_method" factory-bean="studentDao"></bean>
1.6、基于注解的实现过程
1.6.1、配置注解扫描
<!--会扫描指定包及所有子包-->
<context:component-scan base-package="指定扫描的包"></context:component-scan>
1.6.2、注解
1. 添加在类上
@Component:表示将类标记为容器中的一个bean;
@Service // service层
@Controller// controller层
@Repository // dao层
@Scope(scopeName="singleton") //单例对象
@Scope(scopeName="prototype") //多例对象
2. 添加在属性上
@Value("属性值")
@Autowired
@Qualifier("bean name"):一个接口类型同时有两个实现类,@Autowired会报错,使用@Qualifer指定bean;
3. 添加在方法上
@PostConstruct:等价于init-method属性;
@PreDestroy:等价于destroy-method属性;
2、DI
DI,Dependency Injection,即依赖注入。当容器创建bean实例时,将bean实例属性的值注入。
2.1 Set注入
1. 基本属性类型值
<property name="" value=""/>
2. 引用属性类型值
<property name="" ref="bean id">
注意:类中需要定义set方法。
2.2 构造注入
1. 通过name属性,按照参数名赋值
<bean id="StudentDao1" class="com.zhy.dao.StudentDao">
<constructor-arg name="name" value="周大宇"></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
</bean>
2. 通过index属性,按照参数索引赋值,从0开始
<bean id="StudentDao2" class="com.zhy.dao.StudentDao">
<constructor-arg index="0" value="周大宇"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
</bean>
3. 通过type属性,按照参数类型赋值
<bean id="StudentDao3" class="com.zhy.dao.StudentDao">
<constructor-arg type="java.lang.String" value="周三宇"></constructor-arg>
<constructor-arg type="int" value="24"></constructor-arg>
</bean>
对应实体类:
public class StudentDao{
private String name;
private int age;
}
2.3 spring el 表达式
<bean id="classDao" class="com.zhy.dao.ClassDao">
<property name="classId" value="1001"></property>
</bean>
<bean id="studentDao" class="com.zhy.dao.StudentDao" p:classDao-ref="classDao">
<property name="name" value="周大宇"></property>
<property name="age" value="20"></property>
<property name="classDao" value="#{classDao.classId}"></property>
</bean>
2.4 p命名空间
<bean id="studentDao" class="com.zhy.dao.StudentDao" p:name="周大宇" p:age="20"></bean>
2.5 复杂类型
实体类:
public class Demo {
private Object[] arr;
private ArrayList list;
private Set set;
private Map map;
private Properties properties;
setter、getter省略
}
<bean id="studentDao" class="com.zhy.dao.StudentDao"></bean>
<bean id="demo" class="com.zhy.dao.Demo">
<!--数组-->
<property name="arr">
<list>
<value>数组值1</value>
<value>数组值2</value>
<value>数组值3</value>
<ref bean="studentDao"/>
</list>
</property>
<!--list集合-->
<property name="list">
<list>
<value>list集合值1</value>
<value>list集合值2</value>
<value>list集合值3</value>
<list>
<value>集合中的集合值1</value>
<value>集合中的集合值2</value>
<value>集合中的集合值3</value>
</list>
<ref bean="studentDao"></ref>
</list>
</property>
<!--Set-->
<property name="set">
<set>
<value>Set集合值1</value>
<value>Set集合值2</value>
<value>Set集合值3</value>
<ref bean="studentDao"></ref>
</set>
</property>
<!--map-->
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
<entry key="k3" value="v3"></entry>
<entry key="ref" value-ref="studentDao"></entry>
</map>
</property>
<!--properties-->
<property name="properties">
<props>
<prop key="name">周大宇</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
2.6 自动注入
<bean id="" class="" autowire=""></bean>
autowire属性:
no:不自动装配;
byName:通过name自动装配(属性的名称=bean的id),调取set方法赋值;
byType:通过类型自动装配(属性的类型=bean的类型),当有多个相同类型的对象时会报错,调取set方法赋值;
constructor:通过构造方法自动装配(构造方法参数的类型=bean的类型);
3、AOP
AOP,Aspect Oriented Programming,即面向切面编程。在不改变原程序的基础上为代码段增加新的功能。应用在权限认证、日志、事务。
3.1 JDK动态代理
JDK动态代理,针对实现了接口的类产生代理,代理类实现InvacationHandler接口。
接口:
public interface JdkService {
void login();
}
实现类:
public class JdkServiceImpl implements JdkService {
@Override
public void login() {
System.out.println("真实方法执行");
}
}
代理类:
public class JdkProxy implements InvocationHandler {
//目标对象
private JdkService target;
public JdkProxy(JdkService target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强");
//被代理的类
Object invoke = method.invoke(target, args);
System.out.println("后置增强");
return invoke;
}
}
测试:
@Test
public void jdkProxyTest(){
//真实角色
JdkService jdkService = new JdkServiceImpl();
//代理角色
JdkProxy jdkProxy = new JdkProxy(jdkService);
JdkService service = (JdkService) Proxy.newProxyInstance(jdkService.getClass().getClassLoader(), jdkService.getClass().getInterfaces(), jdkProxy);
service.login();
}
3.2 CGLIB动态代理
CGlib动态代理,针对没有实现接口的类产生代理,应用的是底层的字节码增强技术,生成当前类的子类对象,实现MethodInterceptor接口。
添加依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
普通类:
public class User {
public void login(){
System.out.println("真实方法执行");
}
}
代理类:
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("后置增强");
return obj;
}
}
测试:
@Test
public void cglibProxyTest(){
//真实角色
User user = new User();
//代理角色
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(user.getClass());
enhancer.setCallback(new CglibProxy());
User u = (User) enhancer.create();
u.login();
}
3.3 Spring中使用AOP
aop:before(前置通知):目标方法运行之前调用;
aop:after-returning(后置通知):目标方法运行之后调用,出现异常则不会调用;
aop:around(环绕通知):在目标方法前后调用;
aop:after(最终通知):目标方法运行之后调用;
aop:after-throwing(异常通知):程序出现异常时执行;
添加依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
接口:
public interface UserDao {
void test1();
}
public interface UserService {
void test1();
void test2();
}
实现类:
public class UserDaoImpl implements UserDao {
@Override
public void test1() {
System.out.println("UserDaoImpl---test1");
}
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void test1() {
userDao.test1();
}
@Override
public void test2() {
System.out.println("UserServiceImpl---test2");
int i= 10/0;
}
}
切面:
public class MyAspect {
public void before(){
System.out.println("前置增强");
}
public void after_returning(){
System.out.println("后置增强");
}
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕增强前");
point.proceed();
System.out.println("环绕增强后");
}
public void after(){
System.out.println("最终增强");
}
public void after_throwing(){
System.out.println("异常增强");
}
}
配置文件:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userDao" class="com.springaop.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.springaop.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="myAspect" class="com.springaop.aspect.MyAspect"></bean>
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.springaop.service.impl.*.*(..))"/>
<aop:aspect ref="myAspect">
<aop:before method="before" pointcut-ref="mypoint"/>
<aop:after-returning method="after_returning" pointcut-ref="mypoint"/>
<aop:around method="around" pointcut-ref="mypoint"/>
<aop:after method="after" pointcut-ref="mypoint"/>
<aop:after-throwing method="after_throwing" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
</beans>
测试类:
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test1();
userService.test2();
}
4、Spring事务管理
Spring的事务和数据库事务基本一致。
4.1 事务的传播行为
Spring事务传播行为详解
4.1.1 @Transactional属性
4.1.2 Demo
bean:
public class Account implements Serializable{
private Integer id;
private String name;
private Float money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" + "id=" + id + ", name='" + name + '\'' + ", money=" + money + '}';
}
}
dao层:
public interface AccountDao {
/**
* 根据id查找账户信息
* @param id
* @return
*/
Account findAccountById(Integer id);
/**
* 根据名称查找账户信息
* @param name
* @return
*/
Account findAccountByName(String name);
/**
* 跟新账户信息
* @param account
*/
void updateAccount(Account account);
dao实现类:
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/**
* 根据id查找账户信息
*
* @param id
* @return
*/
@Override
public Account findAccountById(Integer id) {
String sql = "select * from account where id = ?";
List<Account> list = getJdbcTemplate().query(sql, new AccountRowMapper(), id);
return list.isEmpty()?null:list.get(0);
}
/**
* 根据名称查找账户信息
*
* @param name
* @return
*/
@Override
public Account findAccountByName(String name) {
String sql = "select * from account where name = ?";
List<Account> list = getJdbcTemplate().query(sql, new AccountRowMapper(), name);
if (list.isEmpty()){
return null;
}
if(list.size()>1){
throw new RuntimeException("账户不唯一");
}
return list.get(0);
}
/**
* 跟新账户信息
*
* @param account
*/
@Override
public void updateAccount(Account account) {
String sql = "update account set money=? where id= ?";
getJdbcTemplate().update(sql,account.getMoney(),account.getId());
}
}
service层:
public interface AccountService {
/**
* 根据id查找账户信息
* @param id
* @return
*/
Account findAccountById(Integer id);
/**
* 转账
* @param sourceName
* @param targeName
* @param money
*/
void transfer(String sourceName,String targeName,Float money);
}
service层实现类:
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 根据id查找账户信息
*
* @param id
* @return
*/
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
/**
* 转账
*
* @param sourceName
* @param targeName
* @param money
*/
@Override
public void transfer(String sourceName, String targeName, Float money) {
//根据名称查找两个账户
Account source = accountDao.findAccountByName(sourceName);
Account targe = accountDao.findAccountByName(targeName);
//修改两个账户的金额
source.setMoney(source.getMoney()-money);
targe.setMoney(targe.getMoney()+money);
//跟新转账之后的两个账户
accountDao.updateAccount(source);
//出现异常
int i = 10/0;
accountDao.updateAccount(targe);
}
}
RowMapper实现类:
public class AccountRowMapper implements RowMapper<Account> {
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();
account.setId(resultSet.getInt("id"));
account.setName(resultSet.getString("name"));
account.setMoney(resultSet.getFloat("money"));
return account;
}
}
配置文件:
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--加载db.properties-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<!--配置jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="accountDao" class="dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试类:
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
service.transfer("周大宇","周二宇",10000f);
}
}