目录
1.使用@Autowired注解进行引用类型装配(运用该方法可以不提供set方法)
6.将通知类交给Spring管理,并在配置类中开启AOP注解驱动
一.Spring是什么
Spring是一种轻量级开源框架,以IoC(反转控制)和AOP(面向切面编程)为内核。
可以提供了展现层和持久层,以及业务层事务管理的一种使用最多的企业应用开源框架。
Spring的理念:无入侵式编程。
二.Spring的优势
1.方便解耦,简化开发
通过Spring提供的容器,将对象间的关系交由Spring进行控制,避免硬编码所造成的耦合。
2.AOP编程的支持
方便进行面向切面编程
3.声明式事务的支持
灵活进行事务管理,提高开发效率
4.方便程序的调试
可以不依赖于容器而进行几乎所有的测试工作
5.方便集成各种优秀框架
支持其他框架
6.降低JavaEE API的使用难度
容易封装API,也降低了使用难度
7.Java源码是经典学习的典范
三.Spring的体系结构
四.Spring程序的开发步骤
1.导入Spring开发的基本包坐标
2.编写Dao接口和实现类
3.创建Spring核心配置文件
4.在Spring配置文件中配置Dao层接口
5.使用Spring的API获得Bean实例
【注】:在第四步中可以较为方便的,整体化的更改Dao层接口
五.配置文件的一些属性说明
1.bean:用来集合创建的类(Spring默认创建单例)
具有的值有id,class,name
scope:singleton(单例),prototype(非单例)
适合交给容器进行管理的bean
表现层对象 |
业务层对象 |
数据层对象 |
工具对象 |
不适合交给容器进行管理的bean
封装实体的域对象 |
bean的实例化
1.用无参构造器创建对象。
<!-- 方法一:使用构造方法实例化bean-->
<bean id="userDao" class="com.example.springdemo.dao.impl.UserDaoImpl"/>
2.factory-method属性结合静态工厂创建对象:静态工厂中要有getXXX的方法,
且需要时静态方法。
<!-- 方法二:使用静态工厂实例化bean-->
<bean id="userDao" class="com.example.springdemo.factory.UserFactory" factory-method="getUserDao"/>
3.factory-bean结合实例工厂实例化:先创建工厂,再创建对象。
构造方法是实例方法。
<!-- 方法三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.example.springdemo.factory.UserFactory" />
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
4.使用FactoryBean实例化bean:要继承一个Factory的接口实现其中的方法,
在配置文件里就像配置普通bean一样配置。
<!-- 方法四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.example.springdemo.factory.UserDaoFactoryBean"/>
<bean id="service" class="com.example.springdemo.service.impl.Serviceimpl">
2.property用来配置bean的属性
具有的值有name,ref
六.bean的生命周期
非接口控制
在bean中加入ini()方法和destroy()方法,再在配置处<bean>配置init-method和
destroy-method属性
<bean id="userDao" class="com.example.springdemo.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>
获取bean的时候,会自动执行init()方法,但是destroy方法在程序结束的时候并没有执行,
原因是程序结束的时候,Spring容器并没有正常关闭。
可通过一下方法进行关闭
1.首先将ApplicationContext类换成ClassPathXmlApplicationContext类。
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2.
方法一:使用此类的close()方法直接关闭容器
方法二:使用此类的registerShuidownHook()方法关闭容器
接口控制
将bean继承两个接口:InitializingBean,DisposableBean
七.依赖装配
1.setter注入
引用类型
必须先提供可访问的set方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
再配置中使用property标签ref属性注入引用类型对象
<bean id="userDao" class="com.example.springdemo.dao.impl.UserDaoImpl"/>
<bean id="service" class="com.example.springdemo.service.impl.Serviceimpl">
<property name="userDao" ref="userDao"/>
</bean>
基本类型
必须先提供可访问的set方法
private int Number;
public void setNumber(int number) {
Number = number;
}
再配置中使用property标签ref属性注入值
<bean id="service" class="com.example.springdemo.service.impl.Serviceimpl">
<property name="Number" value="100"/>
</bean>
注意:个人开发模块的时候依赖注入方式推荐使用setter注入
2.构造器注入
3.依赖自动装配
常用装配
通过名称装配
<bean id="userDao" class="com.example.springdemo.dao.impl.UserDaoImpl"/>
<bean id="service" class="com.example.springdemo.service.impl.Serviceimpl" autowire="byName"/>
通过类型装配
<bean id="service" class="com.example.springdemo.service.impl.Serviceimpl" autowire="byType"/>
4.集合注入
数组,List,Set,Map,Property
<bean id="userDao" class="com.example.springdemo.dao.impl.basic">
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
<value>400</value>
</array>
</property>
<property name="list">
<list>
<value>Tang</value>
<value>Tang</value>
<value>Tang</value>
<value>Tang</value>
</list>
</property>
<property name="set">
<set>
<value>Tang1</value>
<value>Tang1</value>
<value>Tang2</value>
<value>Tang2</value>
<value>Tang3</value>
<value>Tang3</value>
</set>
</property>
<property name="map">
<map>
<entry key="1" value="Tang"/>
<entry key="2" value="Tang"/>
<entry key="3" value="Tang"/>
<entry key="4" value="Tang"/>
<entry key="5" value="Tang"/>
</map>
</property>
</bean>
八.加载properties文件
1.加载命名空间
<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"
>
</bean>
2.使用context标签加载properties文件
<!-- 标准格式-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
<!-- 从类路径或jar包搜索,加载properties文件-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
3.读取properties中的信息
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
</bean>
注:properties文件内容
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/dbwebdemo
username=root
password=XXXXX
九.容器
创建容器
1.ClassPathXmlApplicationContext():加载类路径下的配置文件
2.FileSystemXmlApplicationContext():加载文件系统下的配置文件
获取Bean
1.强制转换获取bean
DataSource dataSource = (DataSource) ctx.getBean("dataSource");
2.使用bean的名称并指定类型
DataSource dataSource = ctx.getBean("dataSource",DataSource.class);
3.通过指定特定类型创建bean(缺点:类在properties中只能有一种定义写法)
DataSource dataSource = ctx.getBean(DataSource.class);
注意:ApplicationContext是立即加载类,如果要懒加载,在bean标签处添加lazy-init="true"
<bean id="userDao" class="com.example.springdemo.dao.impl.basic" lazy-init="true"/>
十.注解开发
1.定义bean
使用@Component定义bean(一般用于自己写的类)
@Component("UserDaoImpl")
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
}
@Override
public void Save() {
System.out.println("Save running");
}
}
核心配置文件中通过组件扫描加载bean
<context:component-scan base-package="com.example.springdemo"/>
@Component的三个衍生注解:
1.@Controller:用于表现层bean的定义
2.@Service:用于业务层bean的定义
3.@Repository:用于数据层bean的定义
上述的注解功能和@Component一样,作用是帮助开发者区分这个几个层次
2.纯注解开发
弃用配置文件,将其改为配置类进行配置
@Configuration
public class SpringConfig {
}
再指定扫描范围
@Configuration
@ComponentScan("com.example.springdemo")
public class SpringConfig {
}
获取bean
public class APPForAnnotation {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
UserDao userDao = ctx.getBean("UserDaoImpl", UserDao.class);
userDao.Save();
}
}
3.bean的管理
bean的作用范围
使用@Scope注解进行类的作用范围,可输入singleton或者prototype进行单例和
多例的选择
//单例
@Scope("singleton")
//多例
@Scope("prototype")
bean的生命周期
用标签标记类的初始化和销毁方法
@PostConstruct
public void init(){
System.out.println("UserDao init");
}
@PreDestroy
public void destroy(){
System.out.println("destroy");
}
@PostConstruct表示在构造类之后执行此处的方法
@PreDestroy表示在类销毁前执行该方法
4.依赖注入
1.使用@Autowired注解进行引用类型装配(运用该方法可以不提供set方法)
@Autowired
@Qualifier("UserDaoImpl")
private UserDao userDao;
注:@Qualifier无法单独使用,它用于使@Autowired通过名字去寻找实现类而进行注
入,@Autowired是按类型装配的。
注:自动装配是基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因
此无需提供setter方法
注:自动装配建议使用无参构造器创建对象(默认),如果不提供对应构造方法,请提
供唯一的构造方法
2.使用@Value为基本类型进行初始化注入
首先在对应的值上加上注解@Value
@Value("${ServiceImpl_name}")
private String name;
在配置文件中加上以下内容
ServiceImpl_name=Tang
在配置类中加上注解@PropertySource(其中用字符数组的形式填入值)
@Configuration
@ComponentScan("com.example.springdemo")
@PropertySource({"jdbc.properties"})
public class SpringConfig {
}
3.第三方bean管理
注:第三方bean一般使用@Bean来标注,因为无法修改其源码
新建一个配置类创建第三方类的代码(以Druid为例)
@Value("${JdbcConfig_driver}")
private String driver;
@Value("${JdbcConfig_url}")
private String url;
@Value("${JdbcConfig_userName}")
private String userName;
@Value("${JdbcConfig_password}")
private String password;
@Bean("dataSource")
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
获取只用用getBean()方法直接获取对象
注意:@Bean注解可为其起别名,但属于非必要的操作,以下形式也是可以的
@Value("${JdbcConfig_driver}")
private String driver;
@Value("${JdbcConfig_url}")
private String url;
@Value("${JdbcConfig_userName}")
private String userName;
@Value("${JdbcConfig_password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
主配置类中的写法(导入式)
@Configuration
@ComponentScan("com.example.springdemo")
@PropertySource({"jdbc.properties"})
@Import({JdbcConfig.class})
public class SpringConfig {
}
十一.整合Mybatis和Junit
1.Mybatis
首先分离mybatis的配置,将其单独分离出来,单独成一个类。在其中写SqlSession的对象
public class Mybatis {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
//用以下类将查询出来的信息包装
ssfb.setTypeAliasesPackage("com.example.springdemo.pojo");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.example.springdemo.dao");
return msc;
}
}
DataSource的配置见十.注解开发.4.依赖注入.3.第三方bean的管理
UserDao接口
@Repository
public interface UserDao {
@Select("select * from tb_user where Id = #{id}")
User FindById(int id);
}
User
public class User implements Serializable {
private String userName;
private String userPassword;
public User() {
}
public String getUserName() {
return userName;
}
public void setUserName(String name) {
this.userName = name;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
@Override
public String toString() {
return "User{" +
"name='" + userName + '\'' +
", password='" + userPassword + '\'' +
'}';
}
}
Serve接口的实现类
@Service
public class ServiceImpl implements Serve{
@Autowired
private UserDao userDao;
@Override
public User FindById(int id) {
return userDao.FindById(id);
}
}
接口调用示范
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
Serve serve = ctx.getBean(Serve.class);
System.out.println(serve.FindById(2));
}
2.Junit
在测试类中添加类加载器@RunWith和配置文件加载器@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class ServeTest {
@Autowired
private Serve serve;
@Test
public void FindById(){
System.out.println(serve.FindById(2));
}
}
十二.面向切面编程(AOP)
1.AOP简介及其一些概念
面向切面编程,一种编程范式,指导开发者如何组织程序结构。
作用:在不惊动原始设计的基础上为其进行功能增强
要增加功能的地方叫做切入点
连接点:程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
(在SpringAOP中,理解为方法的执行)
切入点:匹配连接点的式子
在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
一个具体方法:com.XXX.dao包下的某某接口的无形参无返回值的save方法
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的 接口
通知:在切入点处执行的操作,也就是共性功能
在SpringAOP中,功能最终以方法的形式呈现
切面:描述通知与切入点的对应关系
目标对象:原始功能去掉共性功能对应的类产生的对象,这种对象式无法直接完成
最终工作的
代理:目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象
实现
SpringAOP的本质:代理模式
2.AOP注解开发步骤
1.首先导入aop相关坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2.定义dao接口与实现类:参考上方
3.定义通知类,制作通知
public class ServeAdvice {
public void getTime(){
Instant instant = Instant.now();
System.out.println(instant);
}
}
4.定义切入点
public class ServeAdvice {
@Pointcut("execution(void com.example.springdemo.service.Serve.Save())")
private void Pt(){}
public void getTime(){
Instant instant = Instant.now();
System.out.println(instant);
}
}
切入点定义依托于一个不具有任何意义的私有方法进行,无参,无返回值,方法体
无任何实体逻辑
5.绑定切入点与通知关系,并指定通知的执行位置
public class ServeAdvice {
@Pointcut("execution(void com.example.springdemo.service.Serve.Save())")
private void Pt(){}
@After("Pt()")
public void getTime(){
Instant instant = Instant.now();
System.out.println(instant);
}
}
6.将通知类交给Spring管理,并在配置类中开启AOP注解驱动
为通知类打上@Aspect的注解
@Component
@Aspect
public class ServeAdvice {
@Pointcut("execution(void com.example.springdemo.service.Serve.Save())")
private void Pt(){}
@After("Pt()")
public void getTime(){
Instant instant = Instant.now();
System.out.println(instant);
}
}
在配置类中打上@EnableAspectJAutoProxy注解,即开启AOP驱动
@Configuration
@EnableAspectJAutoProxy
public class SpringConfig {
}
3.AOP切入点表达式
切入点:要进行增强的方法
切入点表达式:要进行增强的方法的描述方式
两种描述方式:描述接口的表达式,描述实现类的表达式
切入点表达式标准格式:
动作关键字(访问修饰符 返回值 包名.类名/接口名.方法名(参数)异常名)
格式一
/*
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
效果:匹配com.XXX包下的任意包中的UserService类或接口中的所有FindById开头的带有一个参数的方法
*/
execution (public * com.XXX.*.UserService.FindById(*))
格式二
/*
..:多个连续的任意符号,可独立出现,常用于简化包名与参数的书写
匹配com包下的任意包中的UserService类或接口中所有名称为FindById的方法
*/
execution(public void com..UserService.FindById(..))
注解表达式规范
4.AOP通知类型
前置通知 | @Before |
后置通知 | @After |
环绕通知 | @Around |
返回后通知 | @AfterReturning |
抛出异常后通知 | @AfterThrowing |
环绕通知的具体写法(其返回值为Object类型)
有返回值
@Around("Pt()")
public User getTime(ProceedingJoinPoint pjp) throws Throwable {
long t1 = System.currentTimeMillis();
User user = (User) pjp.proceed();
long t2 = System.currentTimeMillis();
System.out.println((t2-t1)/1000.0+"s");
return user;
}
无返回值
@Around("Pt()")
public void getTime(ProceedingJoinPoint pjp) throws Throwable {
long t1 = System.currentTimeMillis();
pjp.proceed();
long t2 = System.currentTimeMillis();
System.out.println((t2-t1)/1000.0+"s");
}
5.AOP通知获取数据
获取切入点方法的参数
JoinPoint:适用于前置,后置,返回后,抛出异常后通知
ProceedJointPoint:适用于环绕通知
通过getArgs()方法
@Around("Pt()")
public User getTime(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
args[0] = 2;
return (User) pjp.proceed(args);
}
获取切入点方法返回值
返回后通知:通过注解来设置
@AfterReturning(value = "Pt()",returning = "user")
public void getUpdate(User user){
System.out.println(user.getUserName()+"你好");
System.out.println("System is Updating...");
}
环绕通知:通过变量来接收
获取切入点方法运行异常信息
抛出异常后通知
环绕通知
十三.Spring事务
一.概念
事务作用:在数据层保障一系列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
事务理解:比如模拟银行转账功能,当一个账户完成减钱操作后,遇到异常了,此时
代码中断,另一个账户的余额并未进行增钱操作,此时就需要事务回滚操作
二.Spring开启事务的步骤
1.添加Spring事务管理
@Transactional
boolean transfer(String InAccount,String OutAccount,Double Money);
2.加入事务管理类
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ptm = new DataSourceTransactionManager();
ptm.setDataSource(dataSource);
return ptm;
}
3.开启注解式事务驱动
@Configuration
@ComponentScan("com.example.springdemo")
@PropertySource({"jdbc.properties"})
@Import({JdbcConfig.class,Mybatis.class})
@EnableAspectJAutoProxy
@EnableTransactionManagement
public class SpringConfig {
}
三.事务角色
事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法
四.事务相关配置
事务配置
事务传播行为