目录
Spring是全栈轻量级开源框架,它的核心是AOP、IOC.
程序的耦合和解耦
传统的MVC模式
逻辑层依赖于持久层;表现层依赖于逻辑层,所以这种耦合程度太高了
还存在编译期报错的情况,因为对象全是new出来的。编译期异常:当没有导入该jar包时就会报出该异常。
使用工厂模式进行解耦
思路:
- 使用配置文件存放全限定类名,使用properties进行存储(key-value),通过key值获取全限定类名,(key相当于spring配置文件中的id,value相当于spring配置文件中的class)
- 使用得到的全限定类名通过反射创建对象(反射机制:动态创建对象,不会出现编译期异常,例如:删除了daoimpl 文件 也不会报出编译异常,只会报出运行时异常)
- 将key和创建的对象存储到一个map中(容器的概念,ioc容器的底层其实也是一个map),实现了单例的效果,容器创建的同时,对象已经创建好了。后面,调用方法就可以从容器中取出对象了
/**
* JavaBean工厂类 读取配置文件,通过读取到的全限定类名创建对象,并且将其存储到map中
*/
public class beanfactory {
private static Properties properties;
private static Map beans;
static {
try {
beans=new HashMap<String,Object>();
properties=new Properties();
InputStream asStream = beanfactory.class.getClassLoader().getResourceAsStream("bean.properties");
properties.load(asStream);
Enumeration<Object> keys = properties.keys();
while (keys.hasMoreElements()){
String key=keys.nextElement().toString();
Object object=Class.forName(properties.getProperty(key)).newInstance();
beans.put(key,object);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public static Object getBean(String className){
return beans.get(className);
}
}
/**
* service的实现类
* 通过使用JavaBean工厂类进行解耦
*/
public class serviceimpl implements Iservice {
Idao idao=(daoimpl)beanfactory.getBean("daoimpl");
@Override
public void Sadd() {
idao.add();
}
}
/**
* web层 创建serviceimpl对象的方法 与上方相同
*/
Spring IOC原理
将JavaBean对象的控制权交给工厂,实现了控制反转
Spring的IOC解决方法
简单搭建spring-maven环境
Maven工厂导入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
编写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="service" class="dome1.service.impl.serviceimpl"></bean>
</beans>
测试代码
public static void main(String[] args) {
//获取容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//从容器中取走对象
serviceimpl service = (serviceimpl) context.getBean("service");
System.out.println(service);//dome1.service.impl.serviceimpl@7d9d1a19
}
获取容器的方法
- ClassPathXmlApplicationContext 从项目根路径下获取配置文件
- FileSystemXmlApplicationContext 从磁盘路径上加载配置文件
- AnnotationConfigApplicationContext 读取注解创建容器对象
ApplicationContext与BeanFactory的区别
BeanFactory是Spring容器的顶层接口,创建对象的时机:什么时候使用什么时候创建对象(适合多例)
ApplicationContext:创建对象的时机是只要读取配置文件,就创建对象(适合单例对象)
public static void main(String[] args) {
//单例对象
ApplicationContext context1 = new ClassPathXmlApplicationContext("bean.xml");
Object service = context1.getBean("service");
//多例对象
ClassPathResource resource = new ClassPathResource("bean.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
Object service1 = beanFactory.getBean("service");
}
bean标签
属性
- id:唯一标识,用于获取对象
- class:指定全限定类名,用于反射创建对象
- scope:指定对象的作用域
- singleton:单例(默认值)
- prototype:多例的
- request:web项目中,spring创建一个javabean对象,将对象存入request域中
- session:web项目中,spring创建一个javabean对象,将对象存入session域中
- global session :应用在集群中
有关生命周期的属性
- init-method:初始化方法
- destroy-method:销毁方法
示例代码:bean的生命周期
bean配置
<bean id="service" class="dome1.service.impl.serviceimpl" init-method="init" destroy-method="display">
测试代码
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Object service = context.getBean("service");
context.close();//手动关掉容器
}
控制台输出
单例对象:与容器相关,创建容器的同时对象就会被创建,销毁容器时对象就会被销毁。
多例对象:当使用对象时才会被创建,只要对象在使用,就一直活着;当对象长时间不用时,就会被java的垃圾回收器回收了
实例化Bean的三种方式
- 使用无参构造(默认):bean标签默认根据无参构造创建类对象
- 使用实例工厂的方法创建对象
- 使用静态工厂的方法创建对象
<?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="dome1" class="dome1.service1.dome1"/>
<!-- 实例工厂(两步):先将工厂对象放进spring 在从方法中获取对象-->
<bean id="dome2" class="dome1.service1.dome2"/>
<bean id="dome21" factory-bean="dome2" factory-method="getDome1"/>
<!-- 静态工厂 直接调用方法获取对象-->
<bean id="dome3" class="dome1.service1.dome3" factory-method="getDome"/>
</beans>
依赖注入
构造方法注入
bean标签内使用constructor标签
属性
- name:构造方法的形参名称
- type:构造方法的形参类型(该类型在形参列表中只能使用一次)
- index:构造方法的形参列表的索引
- value:给基本类型和string赋值
- ref:赋的值是bean类型(前提必须在配置文件中配置过)
优点:在获取bean对象时,注入数据是必须的操作,否则无法创建成功
缺点:如果用不到这些数据也必须创建
set注入
bean标签内使用property标签
属性
- name:类的属性名(set方法名 后面的部分)
- ref:给属性赋值是其他的bean类型
- value:给基本类型和string赋值
优点:创建对象时没有明确的限制,可以直接使用默认构造方法
缺点:如果要求某个变量必须有值,这种方法无法保证必须有值,例如:没有调用set方法等等
<!-- 有参构造 构造方法的注入-->
<bean id="now" class="java.util.Date"/>
<bean id="dome11" class="dome1.service1.dome1">
<constructor-arg name="string" value="张三"/>
<constructor-arg type="java.lang.Integer" value="123"/>
<constructor-arg index="2" ref="now"/>
</bean>
<!-- set注入-->
<bean id="dome12" class="dome1.service1.dome1">
<property name="string" value="李四"/>
<property name="date" ref="now"/>
<property name="integer" value="12"/>
</bean>
注入集合
本质:就是set注入
用于给list结构注入的标签:list、set、array
用于给map结构注入的标签:map、props
同一结构的标签可以互换
<!-- 注入集合-->
<bean id="dome4" class="dome1.service1.dome4">
<!-- 数组-->
<property name="strings">
<array>
<value>AAA</value>
<value>BBB</value>
</array>
</property>
<!-- list-->
<property name="list">
<list>
<value>CCC</value>
<value>SSS</value>
</list>
</property>
<!-- set-->
<property name="set">
<set>
<value>WWW</value>
<value>AAA</value>
</set>
</property>
<!-- map-->
<property name="map">
<map>
<entry key="AAA" value="BBBB"/>
<entry key="BBB" value="CCC"/>
</map>
</property>
<!-- pro-->
<property name="properties">
<props>
<prop key="AAA">BBB</prop>
<prop key="WWW">CCC</prop>
</props>
</property>
</bean>
案例一:利用MVC架构,实现数据库基本操作
分析:
public class daoimpl implements Iuser {
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
@Override
public void addUser(user user) {
try {
runner.insert("insert into user1 values(?,?,?,?)",new BeanHandler<user>(dome3.dmain.user.class),user.getId(),user.getName1(),user.getAge(),user.getSex());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void delUser(int id) {
try {
runner.update("delete from user1 where id=?",id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public void udpateUser(user user) {
try {
runner.update("update user1 set id=?,name1=?,age=?,sex=? where id=?",user.getId(),user.getName1(),user.getAge(),user.getSex(),user.getId());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@Override
public List<user> list_user() {
List<user> query=null;
try {
query = runner.query("select * from user1", new BeanListHandler<>(user.class));
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return query;
}
@Override
public user findById(int id) {
user query=null;
try {
query=runner.query("select * from user1 where id=?", new BeanHandler<>(user.class),id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return query;
}
}
配置文件
<?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="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value=""/>
<property name="password" value=""/>
<property name="jdbcUrl" value="jdbc:mysql:///db_6"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!-- 配置QueryRunner jdbc的封装-->
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg name="ds" ref="dataSource"/>
</bean>
<!-- dao对象-->
<bean id="dao" class="dome3.dao.impl.daoimpl">
<property name="runner" ref="runner"/>
</bean>
<!-- service对象-->
<bean id="service" class="dome3.service.impl.serviceimpl">
<property name="iuser" ref="dao"/>
</bean>
</beans>
基于注解的IOC配置
创建对象:将对象存入ioc容器中
@Component | 相当于bean标签 |
@Contrller | 语义化注解:用于表现层 |
@Service | 语义化注解:用于业务层 |
@Repository | 语义化注解:用于持久层 |
注入数据
@Autowired | 自动按照类型注入 |
@Qualifier | 成员变量:在自动按照类型注入的基础上,再按照Bean的id注入,必须和@Autowired使用 方法参数:可以独立使用 |
@Resource | 属性:name 直接按照Bean的id注入(相当于@Autowired+@Qualifier) |
@Value | 注入基本数据类型 |
@Autoired的原理
从spring的ioc容器中寻找是否有相同数据类型的数据,有则直接注入;
如果该类型数据不止一个,那么根据Bean的id进行注入(类成员变量=Bean的id)
@Autowired、@Qualifier、@Resource只能注入JavaBean类型的数据
改变作用范围
@Scpe | 指定bean的作用范围 相当于scope属性 |
与生命周期相关的注解(定义在方法上)
@PostConstruct | 指定初始方法 |
@PreDestroy | 指定销毁方法 |
示例代码
@Component("test")
public class tset1 {
// @Autowired//自动扫描注入 根据数据类型自动注入
// @Qualifier("u1")//在按照数据类型的基础上 按照指定的Beanid进行注入
@Resource(name = "u2")//@Resource=@Autowired+@Qualifier 将在Ioc容器中 user类型中id=u2 的数据注入
private dome2.t1.dao.user u22;
public void T_show(){
u22.show();
}
@Value("张三")//注入常量数据
public String name;
@PostConstruct//指定初始化方法
public void init(){
System.out.println("init...");
}
@PreDestroy//指定销毁方法
public void del(){
System.out.println("del...");
}
}
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="dome2.t1.dao"/>
</beans>
新注解
新注解的作用:可以使用配置类完全代替配置文件 配置类可以创建外部jar包中的类对象,并且将对象存放到ioc容器中
配置文件和配置类的对应关系
@Configuration | 指定当前类为配置类,可以替代xml配置文件 |
@ComponentScan | 指定spring在初始化时要扫描的包 |
@Bean | 属性:name 使用在方法上,表明使用此方法创建一个对象,并且将其放入spring容器中 |
@PropertySource | 用于加载properties文件 value:指定文件位置,如果是类路径下,需要添加classpath |
@Import | 用于导入其它配置类 |
@Import:
将用于导入其它配置类(父子关系)
场景:主配置文件中不写任何东西,只用来导入其它配置类
示例代码:用注解形式实现上一个案例
//主配置类
@Configuration//定义配置类
@ComponentScan("dome3")//扫描包
@Scope("")
@Import(config1.class)//导入config1配置类
public class config {
}
@PropertySource("classpath:jdbc.properties")//加载properties文件
public class config1 {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String user;
@Value("${jdbc.password}")
private String pwd;
// 配置数据源
@Bean(name = "dataSource")//将外部jar包类对象 创建并存储在ioc容器中
public DataSource getDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(user);
dataSource.setPassword(pwd);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
// 配置QueryRunner
@Bean(name = "runner")
public QueryRunner runner(@Qualifier("dataSource") DataSource dataSource){//将容器中beanId=dataSource 的数据注入到方法的参数中
return new QueryRunner(dataSource);
}
}
Spring整合Junit
作用:想要在测试类中直接使用SpringIoc容器中的数据,让程序自动创建容器
添加坐标
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
junit的版本一定要大于等于4.12
测试类的示例代码
@RunWith(SpringJUnit4ClassRunner.class)//替换原有的运行器
@ContextConfiguration(classes = config.class)//设置指定配置文件 或者配置类
//classes:用于指定配置类 locations:用于指定配置文件,需要用'classpath:'表明类路径
public class t2 {
@Autowired
private Iservice iservice;//从ioc文件中 获取对象
@Test
public void t_1(){
List<user> users = iservice.list_user();
for (user user : users) {
System.out.println(user);
}
}
}