[Spring Framework]
系统架构
Spring Framework是spring家族中的底层框架及设计框架
核心概念&&入门案例 IOC(Inversion of Control)控制反转 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到 外部,此思想称为控制反转。
Spring与IOC的关系:Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的”外部”。
IOC容器中存放的是一个个数据层和业务层的bean对象
DI(Dependency Injection)依赖注入 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
IOC实现案例
创建项目:
pom.xml添加Spring的依赖jar包
1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.2.10.RELEASE</version > </dependency > </dependencies >
创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public interface BookDao { public void save () ; } public class BookDaoImpl implements BookDao { public void save () { System.out .println("book dao save ..." ); } } public interface BookService { public void save () ; } public class BookServiceImpl implements BookService { private BookDao bookDao = new BookDaoImpl(); public void save () { System.out .println("book service save ..." ); bookDao.save(); } }
resources下添加spring配置文件,并完成bean的配置
1 2 <bean id ="bookDao" class ="dao.impl.BookDaoImpl" /> <bean id ="bookService" class ="service.impl.BookServiceImpl" />
使用Spring提供的接口完成IOC容器的创建并加载配置文件
1 2 3 4 5 6 7 8 9 10 11 12 public static void main(String[] args) { // 获取IOC容器 // 1 .加载类路径下的配置文件,默认为立即加载 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml" ); // 2 .从文件系统下加载配置文件 // ApplicationContext ctx = new FileSystemXmlApplicationContext("绝对路径" ); // 3 .已过时,延迟加载 // Resource resources = new ClassPathResource("applicationContext.xml" ); // BeanFactory bf = new XmlBeanFactory(resources); }
从容器中获取对象进行方法调用(获取bean)
1 2 3 4 5 6 7 8 // 在主方法下接着上文:// 法一:// BookService bookService = (BookService) ctx.getBean("bookService" );// 法二:// BookService bookService = ctx.getBean("bookService" ,bookDao.class);// 法三:BookService bookService = ctx.getBean(bookDao.class); bookService.save();
DI入门案例 (项目创建在下文中不再讲解)
删除业务层中使用new的方式创建的dao对象
在业务层提供BookDao的setter方法
在配置文件中添加依赖注入的配置
1 2 3 4 5 6 7 8 9 <bean id ="bookDao" class ="dao.impl.BookDaoImpl" /> <bean id ="bookService" class ="service.impl.BookServiceImpl" > <property name ="bookDao" ref ="bookDao" /> </bean >
运行程序调用方法
bean配置,实例化及生命周期 bean配置
bean的别名
1 2 3 4 5 <bean id="bookService" name ="service service2 bookEbi" class ="service.impl.BookServiceImpl"> <property name ="bookDao" ref ="bookDao"/> </bean> //用name 标签给bean起别名并在main方法中修改,如下
1 2 3 4 5 public static void main (String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext ("applicationContext.xml" ); BookService bookService = (BookService) ctx.getBean("service" ); bookService.save(); }
bean的作用范围
1 2 <bean id ="bookDao" name ="dao" class ="dao.impl.BookDaoImpl" scope ="singleton" /> <-- 添加标签scope限制作用范围,singleton为单例,prototype为非单例-->
适合交给spring管理的bean对象:表现层对象 业务层对象 数据层对象 工具对象
bean实例化
用无参构造方法来实例化对象
1 2 3 4 public BookDaoImpl () { System.out .println("book dao constructor is running ...." ); }
使用静态工厂实例化对象
1 2 3 4 5 6 7 public class OrderDaoFactory { public static OrderDao getOrderDao () { System.out .println("factory setup...." ); return new OrderDaoImpl(); } }
1 <bean id ="orderDao" class ="factory.OrderDaoFactory" factory-method ="getOrderDao" />
使用实例工厂实例化bean
1 2 3 4 5 //创建实例工厂对象(main方法中) UserDaoFactory userDaoFactory = new UserDaoFactory() //通过实例工厂对象创建对象 UserDao userDao = userDaoFactory.getUserDao() userDao.save()
1 2 3 4 5 6 public class UserDaoFactory { public UserDao getUserDao () { return new UserDaoImpl(); } }
1 2 3 <bean id ="userFactory" class ="factory.UserDaoFactory" /> <bean id ="userDao" factory-method ="getUserDao" factory-bean ="userFactory" />
使用FactoryBean实例化bean
创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
1 2 3 4 5 6 7 8 9 10 public class UserDaoFactoryBean implements FactoryBean <UserDao > {public UserDao getObject () throws Exception {return new UserDaoImpl();} public Class<?> getObjectType() {return UserDao.class ;} }
1 <bean id ="userDao" class ="factory.UserDaoFactoryBean" />
1 2 3 4 5 T getObject () throws Exception ;Class<?> getObjectType(); default boolean isSingleton () {return true ;}
bean生命周期 方法一
添加初始化和销毁方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public class BookDaoImpl implements BookDao {public void save () {System.out .println("book dao save ..." ); } public void init () {System.out .println("init..." ); } public void destory () {System.out .println("destory..." ); } }
配置生命周期
1 2 <bean id= "bookDao" class= "dao.impl.BookDaoImpl" init-method= "init" destroy-method = "destory" />
两种方法关闭容器
1 2 3 4 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml" ) //调用ctx的close方法 ctx.close()
1 ctx.registerShutdownHook()
方法二 修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的 两个方法afterPropertiesSet和destroy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class BookServiceImpl implements BookService , InitializingBean , DisposableBean {private BookDao bookDao;public void setBookDao (BookDao bookDao ) {this .bookDao = bookDao;}public void save () {System.out .println("book service save ..." ); bookDao.save();} public void destroy () throws Exception {System.out .println("service destroy" );} public void afterPropertiesSet () throws Exception {System.out .println("service init" );} }
注入问题 两种常见的依赖注入方式:强制注入(Setter 注入)和构造器注入
[1]. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现,强制依赖指对象在创建的过程中必须要注入指定的参数
[2]. 可选依赖使用setter注入进行,灵活性强,可选依赖指对象在创建过程中注入的参数可有可无
[3]. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相 对严谨
[4]. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选 依赖的注入
[5]. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
[6]. 自己开发的模块推荐使用setter注入
setter引用类型注入(在实现类中提供setter方法) 1 2 3 <bean id= "" class= "" > <property name= "" ref= "" /> </bean>
setter简单类型注入(在实现类中提供setter方法) 1 2 3 <bean id= "" class= "" > <property name= "" value= "" /> </bean>
构造器注入引用类型(在实现类中提供构造方法) 1 2 3 <bean id= "" class= "" > <constructor-arg name= "" index= "" type= "" ref= "" /> </bean>
构造器注入简单类型(在实现类中提供构造方法) 1 2 3 <bean id= "" class= "" > <constructor-arg name= "" index= "" type= "" value= "" /> </bean>
依赖自动装配(提供setter方法)
按类型,类型匹配唯一
1 2 3 4 <bean class ="dao.impl.BookDaoImpl" /> <bean id ="bookService" class ="service.impl.BookServiceImpl" autowire ="byType" />
按名称,具有指定名称的bean
1 2 3 4 <bean class ="dao.impl.BookDaoImpl" /> <bean id ="bookService" class ="service.impl.BookServiceImpl" autowire ="byName" />
集合注入
array注入
1 2 3 4 5 6 7 <bean id ="" class ="" > <property name ="array" > <array > <value > ...</value > </array > </property > </bean >
list注入
1 2 3 4 5 6 7 <bean id ="" class ="" > <property name ="list" > <list > <value > ...</value > </list > </property > </bean >
set注入
1 2 3 4 5 6 7 <bean id ="" class ="" > <property name ="set" > <set > <value > ...</value > </set > </property > </bean >
map注入
1 2 3 4 5 6 7 <bean id ="" class ="" > <property name ="map" > <map > <entry key ="" value ="" /> </map > </property > </bean >
properties注入
1 2 3 4 5 6 7 <bean id ="" class ="" > <property name ="properties" > <props > <prop key ="" > ...</prop > </props > </property > </bean >
容器 容器类结构层次
IOC/DI注解开发IOC/DI注解开发 注解开发定义bean
在需要添加注解的表现层、业务层或是数据层的类上添加注解
1 2 3 4 5 6 7 8 9 @Component("..." ) public class ...impl implements ...
配置Spring的注解包扫描
1 <context :component -scan base-package ="包名" />
从IOC容器中获取对应的bean对象
1 2 3 4 5 6 public static void main (String [] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext ("applicationContext.xml" );BookService bookService = ctx.getBean (BookService.class ); }
纯注解开发模式,使用Java类替代配置文件
将配置文件applicationContext.xml删除掉
创建一个配置类SpringConfig,标识该类为配置类,并用注解替换包扫描配置
1 2 3 4 @Configuration @ComponentScan ("包名" )public class SpringConfig { }
运行
1 2 3 4 5 6 7 8 public class AppForAnnotation {public static void main (String [] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext (SpringConfig.class );BookDao bookDao = (BookDao) ctx.getBean ("bookDao" ); } }
注解开发bean作用范围与生命周期管理
管理是否单例
1 2 3 4 5 6 @Repository @Scope ("prototype" )public class ...Impl implements ... { } }
生命周期
1 2 3 4 5 6 7 8 9 10 11 12 @Repository public class ...impl implements ...{ @PostConstruct public void init ( ) {System .out .println ("init ..." );} @PreDestroy public void destroy ( ) {System .out .println ("destroy ..." );} }
注意:@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包
1 2 3 4 5 <dependency > <groupId > javax.annotation</groupId > <artifactId > javax.annotation-api</artifactId > <version > 1.3.2</version > </dependency >
注解开发依赖注入
注解实现按照类型注入
1 2 3 4 5 6 7 @Service public class BookServiceImpl implements BookService {@Autowired private BookDao bookDao;} }
注解实现按照名称注入
1 2 3 4 5 6 7 @Service public class BookServiceImpl implements BookService { @Autowired @Qualifier ("bookDao1" )private BookDao bookDao; }
简单数据类型注入
1 2 3 4 5 @Repository("bookDao" ) public class BookDaoImpl implements BookDao {@Value("..." ) private String name;}
注解读取properties配置文件
1 2 3 4 5 @Configuration @ComponentScan ("包名" )@PropertySource ("配置文件.properties" )public class SpringConfig { }
1 2 3 4 5 6 7 8 @Repository("bookDao" ) public class BookDaoImpl implements BookDao {@Value("${...} " ) private String name;}
注解开发管理第三方bean,以下以druid连接池为例
现在pom文件中导入druid依赖
对于数据源的bean,我们新建一个JdbcConfig配置类,并把数据源配置到该类下
1 2 3 4 5 6 7 8 9 10 11 public class JdbcConfig {@Bean public DataSource dataSource () {DruidDataSource ds = new DruidDataSource ();ds.setDriverClassName("com.mysql.jdbc.Driver" ); ds.setUrl("jdbc:mysql://localhost:3306/spring_db" ); ds.setUsername("root" ); ds.setPassword("root" ); return ds;} }
将该配置类引入到spring配置类中
方法一:使用包扫描引入(不推荐使用)
在Spring的配置类上添加包扫描
1 2 3 4 @Configuration @ComponentScan ("config" )public class SpringConfig { }
在JdbcConfig上添加配置注解
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class JdbcConfig {@Bean public DataSource dataSource () {DruidDataSource ds = new DruidDataSource ();ds.setDriverClassName("com.mysql.jdbc.Driver" ); ds.setUrl("jdbc:mysql://localhost:3306/spring_db" ); ds.setUsername("root" ); ds.setPassword("root" ); return ds;} }
方法二:使用@Import引入
在spring配置类中引入
1 2 3 4 5 6 @Configuration @Import({JdbcConfig.class}) public class SpringConfig {}
注解开发实现为第三方bean注入资源
注入简单类型数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class JdbcConfig { @Value("com.mysql.jdbc.Driver" ) private String driver;@Value("jdbc:mysql://localhost:3306/spring_db" ) private String url;@Value("root" ) private String userName;@Value("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; } }
注入引用类型
1 2 3 4 5 @Configuration @ComponentScan ("dao" )@Import ({JdbcConfig.class})public class SpringConfig { }
1 2 3 4 5 6 7 8 9 10 @Bean public DataSource dataSource(BookDao bookDao ) { System . out.println(bookDao);DruidDataSource ds = new DruidDataSource() ; ds.setDriverClassName(driver ) ; ds.setUrl(url ) ; ds.setUsername(userName ) ; ds.setPassword(password ) ; return ds; }
注解总结:XML配置和注解的区别
Spring整合Mybatis&&Junit 整合Mybatis
在pom文件中导入以下依赖
Spring-context Druid Mybatis Mysql-connector-java Spring-jdbc Mybatis-Spring
创建Spring的主配置类
1 2 3 4 5 6 @Configuration @ComponentScan ("..." )public class SpringConfig { }
创建数据源的配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class JdbcConfig {@Value("${jdbc.driver} " ) private String driver;@Value("${jdbc.url} " ) private String url;@Value("${jdbc.username} " ) private String userName;@Value("${jdbc.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;} }
主配置类中读properties并引入数据源配置类
1 2 3 4 5 6 @Configuration @ComponentScan ("..." )@PropertySource ("classpath:jdbc.properties" )@Import (JdbcConfig.class)public class SpringConfig { }
创建Mybatis配置类并配置SqlSessionFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MybatisConfig {@Bean public SqlSessionFactoryBean sqlSessionFactory (DataSource dataSource) {SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean ();ssfb.setTypeAliasesPackage("domain" ); ssfb.setDataSource(dataSource); return ssfb;} @Bean public MapperScannerConfigurer mapperScannerConfigurer () {MapperScannerConfigurer msc = new MapperScannerConfigurer ();msc.setBasePackage("com.itheima.dao" ); return msc;} }
主配置类中引入Mybatis配置类
1 2 3 4 5 6 @Configuration @ComponentScan ("com.itheima" )@PropertySource ("classpath:jdbc.properties" )@Import ({JdbcConfig.class,MybatisConfig.class})public class SpringConfig { }
从IOC中获取对象并运行
整合junit
引入依赖Junit Spring-test
编写测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RunWith (SpringJUnit4ClassRunner.class)@ContextConfiguration (classes = {SpringConfiguration.class}) 配置文件 public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testFindById (){ System .out .println (accountService.findById (1 ));} @Test public void testFindAll (){System .out .println (accountService.findAll ());} }
Spring_AOP简介 AOP面向切面编程
作用:在不惊动原始设计的基础上为其进行功能增强
Spring理念:无侵入式
SpringAOP本质:代理模式
AOP概念
AOP工作流程
Spring容器启动
读取所有切面配置中的切入点
初始化bean,判定bean对应的类中方法是否匹配到任意切入点
匹配失败,创建对象
匹配成功,创建原始对象(目标对象)的代理对象
获取bean执行方法(获取bean,调用方法执行,完成操作)
AOP切入点表达式
标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
1 execution (public User service.UserService.findById(int ))
访问修饰符:public等,可省略
异常名:方法定义中抛出指定异常,可省略
通配符
1 2 3 4 5 6 execution (public * com.itheima.*.UserService.find*(*))execution (public User com..UserService.findById(..))execution (* *..*Service+.*(..))
AOP通知类型
前置通知 @Before
后置通知@After
环绕通知@Around
返回后通知@AfterReturning
抛出异常后通知@AfterThrowing
重点:环绕通知
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Component @Aspect public class MyAdvice {@Pointcut("execution(dao.BookDao.select())") private void pt2 () {}@Around("pt2()") public Object aroundSelect (ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ..." ); Object ret = pjp.proceed();System.out.println("around after advice ..." ); return ret;} }
AOP通知获取数据 获取参数: 环绕通知 ProceedingJoinPoint方法
Spring事务简介 事务作用:在数据层保障一些列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
事务角色 事务管理员:发起事务方
事务协调员:加入事务方
开启事务注解 @Transactional(xxx,xxx)
事务属性