[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLZGBan8-1602062689251)(.\spring.assets\image-20200902210435932.png)]
先创建一个maven项目
导入对应的maven依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.3.7.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> 对应上面那张图可以看看检查是否导入了jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQ3Iqo7f-1602062689254)(.\spring.assets\image-20200902213928526.png)]
提供UserService接口和实现类
获得UserService实现类的实例
之前开发中,直接new一个对象即可,学习spring之后,将由spring创建对象实例->IOC控制反转,之后需要实例对象时,从spring工厂(容器)中获得,需要将实现类的全限定类的全限定名称配置到xml文件中,至于为什么你想想反射
public interface UserService { void addUser(); } public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("add user "); } }位置:任意,开发中一般在classpath下(src)
名称:任意位置,开发中常用applicationContext
内容:添加schema约束
@Test public void demo01(){ UserService userService=new UserServiceImpl(); userService.addUser(); } @Test public void demo02(){ String xmlPath= "applicationContext.xml"; ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath); //获得内容不需要自己new都是从spring容器获取 UserService userService1= (UserService) applicationContext.getBean("userService"); userService1.addUser(); }依赖注入
is a : 是一个 继承
has a:有一个,成员变量,依赖
class B{
private A a; //B类依赖A类
}
依赖:一个对象需要使用另一个对象
注入:通过set方法进行另一个对象实例的设置
例如: class BookService{
//之前开发: 接口=实现类(service和dao耦合)
private BookDao booDao=new BookDaoImpl();
//spring之后(解耦:service实现类使用dao的接口,不知道具体实现类我可以换)
private BookDao bookDao;
setter方法
}
模拟spring执行过程
创建service实例: BookService bookService=new BookServiceImpl(); —>IOC
创建dao实例:BookDao bookDao=new BookDaoImpl(); ---->IOC
将dao设置给service bookService.setBookDao(bookDao)---->DI
创建BookService接口和实现类
创建BookDao接口和实现类
将dao和service配置到xml文件
常用语spring整合其他框架(或者工具)
静态工厂:用于生成实例对象,所有的方法必须是static-----
<!--将静态工厂创建的实例交给spring class 确定静态工厂全限定类名 factory-method 确定静态方法--> <bean id="user" class="cn.tedu.iocc.StaticFacoryUser" factory-method="getUser"/>实例工厂:必须现有工厂实例对象,通过实例创建对象,提供所有方法都是非静态的
<!--创建工厂实例--> <bean id="userFactory" class="cn.tedu.iocc.InstanceFatory"/> <!--获得user fatory-bean 确定工厂 fatory-metnod 确定普通方法--> <bean id="user" factory-bean="userFactory" factory-method="getUser"/>作用域:用于确定spring创建bean实例的个数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtWaI1lv-1602062689259)(C:\Users\taget\Desktop\spring\spring.assets\image-20200904232306741.png)]
取值:
singleton 单例,默认值
protorype 多列,没执行一次getBean就会获取一个实例
<!--配置单例还是多列对象 scope="singleton" 单例 scope="prototype" 多列--> <bean id="user" class="cn.tedu.iocdscope.User" scope="prototype"></bean>spring提供一种机制,只要实现此接口BeanPostProcessor,并将实现类提供给spring容器,spring容器将自动执行,在初始化方法前执行befor(),在初始化方法后执行after()
spring提供工厂钩子,用于修改实例对象,可以生成代理对象,是AOP底层
要开启注解扫描
<!--告知spring在创建容器要扫码的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间约束中--> <context:component-scan base-package="service.impl"/>在类上修饰
@Component(value = "accountServiceImpl") public class AccountServiceImpl implements IAccountService { @Override public void saveAccount() { System.out.println("service 执行了"); } } 层级的xml的配置 * <bean id="accountService" class="service.impl.AccountServiceImpl" * scope="" init-method="" destroy-method=""> * <property name="" value=""></property> * </bean> * 用于创建对象的 * 他们的作用就和xml配置文件中编写一个<bean>标签的功能是一样的 * Compoent: * 作用:用于吧当前类对象存入spring容器中 * 属性:value 用于主动bean的id当我们不写时,他默认值是当前类名,且首字母该小写 Controller service Repository 以上三个注解他们的作用和属性与compent一样的 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰 * 用于注入数据的 * 他们的作用就和xml配置文件中的bean标签中写一个<property>标签的作用是一样的 Autowired: 作用:自动按照类型注入,只要容器中有唯一的bean对象类型和要注入的变量类型匹配,就可以注入成功,如果ioc容器中没有任何bean的类型和要注入的变量和类型匹配,则报错,如果ioc容器中有多个类型匹配使用,那么它会按照变量名称来进行匹配,如果我们不想改变量名称就使用Qualifier注解来一起使用 Qualifier: 作用:在按照类中主图的基础上在按照名称注入,它在给类成员注入时不能单独使用,但是在给方法参数注入时可以 属性:value: 用于指定注入bean的id Resource 作用:直接按照bean的id注入,它可以独立使用 属性:name:用于指定bean的id 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现,另外,集合类型的注入只能同过xml来实现 Value 作用:用于注入基本类型和String类型的数据 属性:value:用于指定数据的值,他可以使用spring中的el表达式 spel的写法: ${表达式} * 用于改变作用范围的 * 他们的作用集合在bean标签中使用scope属性实现的功能是一样的 Scope 作用:用于指定bean的作用范围 属性:value: single单例,prototype多列 * 和生命周期相关(了解) * 他们的作用就和在bean标签中使用initmethod和destory-method的作用是一样的 PreDestroy 作用:用于指定销毁方法 PostConstruct 作用:用于指定初始化方法创建对象的注解,compent,controller,service,repostory
@Component(value = "accountServiceImpl") public class AccountServiceImpl implements IAccountService { private AccountDaoImpl accountDao; @Override public void saveAccount() { accountDao.saveAccount(); System.out.println("service 执行了"); } }用于注入数据的注解Autowrite,Qualifier,Resource
@Component(value = "accountServiceImpl") public class AccountServiceImpl implements IAccountService { @Autowired @Qualifier(value = "accountDao") private AccountDaoImpl accountDao; @Override public void saveAccount() { accountDao.saveAccount(); System.out.println("service 执行了"); } } //qualifire修饰属性时不能单独用,他是用来指定id的当出现多个类型相同的bean对象时两个配合使用,如果不想使用这个注解,直接使用Resource也可以准备环境
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies> create database eesy; use eesy CREATE table account(id int PRIMARY KEY auto_increment,name varchar(40),money float)character set utf8 collate utf8_general_ci; INSERT into account(name,money)values('aaa',1000),('bbb',1000),('ccc',1000)service
package cn.tedu.service; import cn.tedu.entity.Account; import java.util.List; public interface IAccountService { /** * 查询所有 */ List<Account> findAllAccount(); /** * 查询一个 * @return */ Account findAccountById(Integer id); /** * 添加一个 * @param account */ void saveAccount(Account account); /** * 更新 */ void updateAccount(Account account); /** * 删除 */ void deleteAccount(Integer id); } package cn.tedu.service.impl; import cn.tedu.dao.IAccountDao; import cn.tedu.entity.Account; import cn.tedu.service.IAccountService; import java.util.List; /** * 账户业务层实现类 */ public class AccountService implements IAccountService { private IAccountDao accountDao; public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override public List<Account> findAllAccount() { return accountDao.findAllAccount(); } @Override public Account findAccountById(Integer id) { return accountDao.findAccountById(id); } @Override public void saveAccount(Account account) { accountDao.saveAccount(account); } @Override public void updateAccount(Account account) { accountDao.updateAccount(account); } @Override public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } }dao
package cn.tedu.dao; import cn.tedu.entity.Account; import java.util.List; public interface IAccountDao { /** * 查询所有 */ List<Account> findAllAccount(); /** * 查询一个 * @return */ Account findAccountById(Integer id); /** * 添加一个 * @param account */ void saveAccount(Account account); /** * 更新 */ void updateAccount(Account account); /** * 删除 */ void deleteAccount(Integer id); } package cn.tedu.dao; import cn.tedu.entity.Account; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import java.sql.SQLException; import java.util.List; public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; public QueryRunner getRunner() { return runner; } public void setRunner(QueryRunner runner) { this.runner = runner; } @Override public List<Account> findAllAccount() { try { return runner.query("select * from account",new BeanListHandler<Account>(Account.class)); } catch (SQLException e) { e.printStackTrace(); } return null; } @Override public Account findAccountById(Integer id) { try { return runner.query("select * from account where id=?",new BeanHandler<Account>(Account.class),id); } catch (SQLException e) { e.printStackTrace(); } return null; } @Override public void saveAccount(Account account) { try { runner.update("insert into account (name,money)value(?,?)",account.getName(),account.getMoney()); } catch (SQLException e) { e.printStackTrace(); } } @Override public void updateAccount(Account account) { try { runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId()); } catch (SQLException e) { e.printStackTrace(); } } @Override public void deleteAccount(Integer id) { try { runner.update("delete from account where id=?",id); } catch (SQLException e) { e.printStackTrace(); } } }配置文件
<?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-3.0.xsd"> <!--配置service--> <bean id="accountService" class="cn.tedu.service.impl.AccountService"> <property name="accountDao" ref="accountDao"/> </bean> <!--配置dao--> <bean id="accountDao" class="cn.tedu.dao.AccountDaoImpl"> <property name="runner" ref="runner"></property> </bean> <!--配置QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean> <!--配置数据源--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!--连接数据库必备信息--> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"/> <property name="user" value="root"/> <property name="password" value="root"/> </bean> </beans>测试
/** * 使用junit单元测试 */ public class AccountServiceTest { @Test public void findAll() { //获取容器 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); IAccountService service= (IAccountService) context.getBean("accountService"); service.findAllAccount().forEach(System.out::print); } @Test public void findOne() { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); IAccountService service= (IAccountService) context.getBean("accountService"); System.out.println(service.findAccountById(1)); }去除bean标签
@Repository public class AccountDaoImpl implements IAccountDao { @Autowired private QueryRunner runner; @Service public class AccountService implements IAccountService { @Autowired private IAccountDao accountDao;这里只是把我们自己写的类给创建了,但是我们导入的jar包里的类,我们无法进行加入注解
去除第三方jar包提供的对象,使用注解
@Configuration public class SpringConfiguration { @Bean("runner") @Scope("prototype") public QueryRunner createaQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } @Bean public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource source=new ComboPooledDataSource(); source.setDriverClass("com.mysql.jdbc.Driver"); source.setJdbcUrl("jdbc:mysql://localhost:3306/eesy"); source.setUser("root"); source.setPassword("root"); return source; }但是这样把配置写死了,于是我们可以使用另外一个注解来读取配置文件PropertySource
@PropertySource("classpath:jdbcConfig.properties") public class JdbcConfiguration { @Value("${jdbc.driver}") private String driverClass; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean("runner") @Scope("prototype") public QueryRunner createaQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } @Bean//表示创建一个对象 public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource source=new ComboPooledDataSource(); source.setDriverClass("com.mysql.jdbc.Driver"); source.setJdbcUrl("jdbc:mysql://localhost:3306/eesy"); source.setUser("root"); source.setPassword("root"); return source; } jdbc.driver=jdbc:mysql://localhost:3306/eesy jdbc.url=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=root当我们有多个配置类的时候,有两种方式来进行加载
第一种,在AnnotationCOnfigApplicationCOntext里面写多个配置类的class
@Test public void testSave() { ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfiguration.class, JdbcConfiguration.class); IAccountService service= (IAccountService) context.getBean("accountService"); service.findAllAccount().forEach(System.out::print); }第二种使用Import来进行导入子配置类
@Configuration @ComponentScan("cn.tedu") @Import(JdbcConfiguration.class) public class SpringConfiguration { @Bean("runner") @Scope("prototype") public QueryRunner createaQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); }Qualifier注解的使用
@Configuration @ComponentScan("cn.tedu") @Import(JdbcConfiguration.class) public class SpringConfiguration { @Bean("runner") @Scope("prototype") //这里隐藏了一个Autowrite注解,这是多数据源切换的用法 public QueryRunner createaQueryRunner(@Qualifier("ds1") DataSource dataSource){ return new QueryRunner(dataSource); } @Bean("dataSource") public DataSource dataSource() throws PropertyVetoException { ComboPooledDataSource source=new ComboPooledDataSource(); source.setDriverClass("com.mysql.jdbc.Driver"); source.setJdbcUrl("jdbc:mysql://localhost:3306/eesy"); source.setUser("root"); source.setPassword("root"); return source; } @Bean("ds1") public DataSource dataSource1() throws PropertyVetoException { ComboPooledDataSource source=new ComboPooledDataSource(); source.setDriverClass("com.mysql.jdbc.Driver"); source.setJdbcUrl("jdbc:mysql://localhost:3306/eesy"); source.setUser("root"); source.setPassword("root"); return source; } }每次都去加载容器,比较麻烦
@Test public void findAll() { //获取容器 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); IAccountService service= (IAccountService) context.getBean("accountService"); service.findAllAccount().forEach(System.out::print); } @Test public void findOne() { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); IAccountService service= (IAccountService) context.getBean("accountService"); System.out.println(service.findAccountById(1)); }改造后
public class AccountServiceTest { ApplicationContext context=null; @Before//每次执行测试方法前都会先去执行它 public void findAll() { //获取容器 context=new ClassPathXmlApplicationContext("applicationContext.xml"); } @Test public void findOne() { IAccountService service= (IAccountService) context.getBean("accountService"); System.out.println(service.findAccountById(1)); } 应用程序的入口 main方法 junit不会管我们是否才有spring框架 在执行测试方法是,junit根本不知道我们是不是使用了spring框架,所以也就不会为我们读取配置文件/配置类创建spring核心容器 由以上得知,当测试方法执行时,没有ioc容器,就算写了autowrite也不会注入整合步骤
导入spring整合junit的jar(坐标)(这里版本不要太高了,本人在这徘徊了大半天)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency>使用junit提供的一个注解吧原有的main方法替换了,替换成spring提供的,加上@Runwith注解
告知spring运行器,spring和ioc创建是基于xml还是注解的,并说明位置 @ContextConfiguration注解
属性:locations:指定xml文件的位置,加上classpath关键字,表示类路径下
classes:指定注解类所在位置
当我们只有spring5.x版本的时候,要修junit必须是4.12及以上
代码演示
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { @Autowired IAccountService service; @Test public void findOne() { // IAccountService service= (IAccountService) context.getBean("accountService"); System.out.println(service.findAccountById(1)); } }