框架是一套规范。
实际是他人实现的一系列接口和类的集合。通过导入对应框架的jar文件(Maven项目导入对应的依赖),进行适当的配置,就能使用其中的所有内容。开发者可以省去很多模板代码
Spring
是一个轻量级开源的Java框架,是一个管理项目中对象的容器,也是其他框架的粘合器,目的就是对项目进行解耦
核心:IOC控制反转和AOP面向切面编程
控制反转是一种思想,就是让创建对象的控制权由自身交给第三方,控制翻转这种思想,通过依赖注入的方式实现
bean标签常用属性
属性 |
作用 |
class |
定义类的全限定名 |
id |
定义对象的名称 |
lazy-init |
设置是否为懒加载,默认值为false,在解析配置文件时就会创建对象。设置为true表示懒加载,只有在getBean()时才会创建对象。 |
scope |
单例/原型模式。默认值为singleton,表示单例模式,只会创建一个对象。设置为prototype,表示原型模式,每次调用getBean()就会创建一个对象 |
init-method |
初始化方法,在创建完该对象时自动调用的方法,该方法只能是无参方法,该属性的值只需要写方法名即可 |
destory-method |
销毁时触发的方法。Spring容器关闭时自动调用的方法,该方法只能是无参方法,只有在单例模式下有效 |
属性注入
给某个bean标签添加属性的两种方式:setter注入和构造器注入
setter注入
该方法注入属性时,类中必须要有set方法
该标签的name属性通常表示该对象的某个属性名,但实际是setXXX()方法中的XXX单词。
该标签的value属性表示给该类中的某个属性赋值,该属性的类型为原始类型或String。
<!--注入Car类对象,并用set方式注入其属性-->
<bean class="com.hqyj.SetterInject.Car" id="c">
<!--该属性是字符串类型或原始类型,使用value-->
<property name="brand" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
构造器注入
这种方式注入属性时,类中必须有相应的构造方法(有参)
该标签的name属性表示构造方法的参数名,index属性表示构造方法的参数索引。
赋值时,原始类型和字符串用value,引用类型用ref。
<!--注入Person类对象 并用构造方法注入其属性-->
<bean class="com.hqyj.SetterInject.Person" id="p2">
<!--constructor-arg表示构造方法参数name是参数名 index是参数索引-->
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg index="1" value="23"></constructor-arg>
<!--c是Car类的对象-->
<constructor-arg name="car" ref="c" ></constructor-arg>
</bean>
复杂属性注入
public class Movie {
private String movieName;//电影名
private String director;//导演
private int duration;//时长
private List<String> playerList;//主演
private String movieType;//电影类型
private String showTime;//放映时间:最终格式为yyyy/MM/dd HH:mm:ss
//忽略了setter/getter/gouzao方法等
}
List类型属性注入
<!--注入movie对象-->
<bean class="com.hqyj.SetterInject.Movie" id="movie1">
<property name="movieName" value="夏洛特烦恼"></property>
<property name="director" value="闫飞彭大魔"></property>
<property name="duration" value="87"></property>
<!--List类型属性赋值-->
<property name="playerList">
<!--使用list标签-->
<list>
<!-- 如果集合中保存的是引用类型,使用ref标签-->
<!--如果集合中保存的是原始类型或字符申,使用value标签-->
<value>沈腾</value>
<value>马丽</value>
<value>艾伦</value>
</list>
</property>
<property name="movieType" value="戏剧"></property>
<property name="showTime" value="2020/06/10 0:0:0"></property>
</bean>
Set类型属性注入
//宠物类
public class Pet {
private String petType;
private String petNickName;
private int petAge;
public Pet(String petType, String petNickName, int petAge) {
this.petType = petType;
this.petNickName = petNickName;
this.petAge = petAge;
}
//省略了setter/getter/toString方法
public Pet() {
}
}
//宠物店类
public class PetShop {
private String shopName;
private Set<Pet> petList;
public PetShop(String shopName, Set<Pet> petList) {
this.shopName = shopName;
this.petList = petList;
}
//省略了setter/getter/toString方法
public PetShop() {
}
}
注入:
<bean class="com.hqyj.SetInject.Pet" id="pet1">
<property name="petType" value="二哈"></property>
<property name="petNickName" value="乐乐"></property>
<property name="petAge" value="3"></property>
</bean>
<bean class="com.hqyj.SetInject.Pet" id="pet2">
<property name="petType" value="金毛"></property>
<property name="petNickName" value="linck"></property>
<property name="petAge" value="2"></property>
</bean>
<bean class="com.hqyj.SetInject.PetShop" id="petShop">
<property name="shopName" value="狗狗乐园"></property>
<property name="petList">
<set>
<ref bean="pet1"></ref>
<ref bean="pet2"></ref>
</set>
</property>
</bean>
Map属性注入
<!--实体类-->
public class Cinema {
private String name;
<!--定义Map保存上映时间表,键是影厅号,值是电影对象-->
private Map<Integer,Movie> timeTable;
public Cinema(String name, Map<Integer, Movie> timeTable) {
this.name = name;
this.timeTable = timeTable;
}
public Cinema() {
}
<!--省略了toString()、getter()、getter()方法-->
}
<!--注入Cinema对象-->
<bean class="com.hqyj.SetterInject.Cinema" id="cinema">
<property name="name" value="万达影城"></property>
<property name="timeTable">
<!--Map类型属性赋值,标明键和值的类型-->
<map key-type="java.lang.Integer" value-type="com.hqyj.SetterInject.Movie">
<!--entry标签表示键值对如果键值对都是原始类型或字符串,使用key和value-->
<!--如果键值对中有引用类型,使用key-ref或value-ref-->
<entry key="1" value-ref="movie1"></entry>
<entry key="2" value-ref="movie2"></entry>
</map>
</property>
</bean>
现在时间注入
<!--注入SimpleDateFormat对象,设置日期格式-->
<bean class="java.text.SimpleDateFormat" id="sdf">
<!--如果构造方法注入时,该构造方法只有一个参数,可以不用name或index-->
<constructor-arg index="0" value="yyyy/MM/dd"></constructor-arg>
</bean>
<!--注入Date对象--> .
<bean class="java.util.Date" id="now"></bean>
<bean class="com.hqyj.SetterInject.Movie" id="movie1">
<property name="类中的时间属性名">
<bean factory-bean="sdf" factory-method="format">
<constructor-arg ref="now"></constructor-arg>
</bean>
</property>
</bean>
属性自动注入autowire
实现自动注入
在某个bean标签中,加入autowire属性,设置值为"byName"或"byType",通常设置为"byType"
public class Car {
private String brand;
private String color;
public Car(String brand, String color) {
this.brand = brand;
this.color = color;
}
public Car() {
}
//省略了setter/getter/toString方法
}
//Person类
public class Person {
private String name;
private int age;
private Car car;
public Person(String name, int age,Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public Person() {
}
//省略了setter/getter/toString方法
}
配置文件:
<!--在容器中注入Car类型的bean-->
<bean class="com.hqyj.SetterInject.Car" id="car">
<property name="brand" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<!--注入Person类的bean-->
<!--autowire="byType "表示自动检测该类中是否需要使用引用类型参数,如果容器中正好有唯一的一个对应类型的bean, 就会自动赋值给对应的属性-->
<bean class="com.hqyj.AutoWire.Person" id="person" autowire="byType">
<property name="name" value="王海"></property>
<property name="age" value="45"></property>
</bean>
<!--autowire="byName"表示自动检测该类中的setxxx方法,如果某个setxxx方法的x和容器某个对象的id对应,就会自动赋值给对应的属性-->
<bean class="com.hqyj.AutoWire.Person" id="person2" autowire="byName">
<property name="name" value="王海"></property>
<property name="age" value="45"></property>
</bean>
主方法:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Person person = context.getBean("person", Person.class);
System.out.println(person);
context.close();
}
autowire属性的值
byType
类中要有被注入的属性的setter()方法
被自动注入的对象可以没有id
Spring容器中,某个对象的类型要与该setter()方法的参数类型-致,且容器中只有一个该类型的对象
如setCar(Car C), Spring就会自动在容器中寻找类型为Car的对象自动装配
byName
类中要有被注入的属性的setter()方法
被自动注入的对象必须要有id
实际是根据setXXX()方法set后的单词XXX关联
如setCar(Car c),Spring就会自动在容器中寻找id为car的对象自动装配
Spring核心注解
在spring配置文件中进行配置:
<!--如果要使用Spring注解,需要加入这段话,表示扫描指定包下所有使用了注解的类-->
<context:component-scan base-package="com.hqyj.Seconds.bookSystem"></context:component-scan>
BookDao类:
//这个注解相当于在配置文件中加入<bean class= "com.hqyj.Seconds.bookSystem.dao.BookDao"></bean>
@Component
publicclassBookDao {
publicvoidqueryAll(){
System.out.println("模拟查询所有图书信息");
}
}
BookService类
@Component//这个注解相当于在配置文件中加入<bean class= "com.hqyj.Seconds.bookSystem.service.BookServicel"></bean>
publicclassBookService {
@Autowired//这个注解相当于<bean class= "com.hqyj.Seconds.bookSystem.service.BookServicel"></bean>中加入autowire=byType
privateBookDaobookDao;
publicvoidshowAll(){
System.out.println("经过service执行dao中的方法");
bookDao.queryAll();
}
}
BookController类
//如果没有设置名称,获取当前类的对象,名称为类名的驼峰俞名法形式。如BookController, 获取时使用bookController获取
@Component
//这里的value表示给该bean设置id名,调用时使用controller
//@Component(value = "controller")
publicclassBookController {
@Autowired
privateBookServicebookService;
publicvoidshowAll(){
System.out.println("经过controller,调用service中的方法");
bookService.showAll();
}
}
Main方法
publicclassBookMain {
publicstaticvoidmain(String[] args) {
ClassPathXmlApplicationContextcontext=newClassPathXmlApplicationContext("book_system.xml");
//BookController类中使用@Component注解时使用
BookControllerbean=context.getBean("bookController",BookController.class);
//BookController类中使用@Component(value="controller")或者@Controller(value = "controller")注解时使用
//BookController bean = context.getBean("controller",BookController.class);
bean.showAll();
}
}
类上加的注解
@Component
当一个类不好归纳时,使用该注解定义为普通组件
@Controller
定义一个类为控制层组件
@Service
定义一个类为业务逻辑层组件
@Repository
定义一个类为持久层(数据访问层/dao层)组件
@Lazy/@Lazy(value=true)
设置该类为懒加载
@Scope/@Scope(valuye="singleton/prototype")
设置为单例/原型模式,默认单例模式
说明
以上注解的公共特点
都是将对应类的对象注入到Spring容器中,用于替换配置文件中的bean标签
默认是单例模式,只会创建一个对象,非懒加载,容器加载时会自动创建。
默认注入的对象id为当前类的类名首字母小写形式
如在BookDao类上添加@Component,id默认为bookDao
可以通过注解的value属性自定义注入的对象的id名,如@Component(value="key")表示注入的对象id为key
属性上加的注解
@Autowired
优先使用byType方式从Spring容器中获取对应类型的对象自动装配,先检索Spring容器中对应类型的数量。如果数量为0,直接报错;如果数量为1直接装配;如果有多个对应类型的对象,会尝试使用byName方式获取对应的id对象,但要配合@Qualifier(value=""某个对象的id)一起使用,指定id进行装配。
@Qualifier(value="某个对象的id")
配合@Autowired注解,使用byName方式获取某个对象id的bean进行装配
@Resource(name="某个对象的id")
该注解相当于@Autowired+@Qualifier(value="某个对象的id")
优先以byName的方式,从spring容器中检索出name为指定名的对象进行装配,如果没有则尝试byType方式,要求对象有且只有一个,否则也会报错。
说明
如果要在某个类中使用spring容器中的某个对象时,只需定义成员变量,无需创建对象,通过@Autowired或@Resource注解进行自动装配
实际开发中,绝大部分情况下,需要自动装配对象有且只有一个,并且命名规范,所以@Autowired或@Resource区别不是很大。@Autowired优先byType方式,@Resource优先使用byName方式。
如果@Resource不能使用,是因为缺少javax.annotation包,需要引入对应依赖
<!--pom.xml-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</ dependency>
web项目中使用Spring
添加依赖
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--spring容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!--web集成-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
在main目录下创建java和resources目录,修改web.xml版本为4.0
在resources目录下创建spring配置文件application.xml,扫描使用了注解的根包
<context:component-scan base-package="com.hqyj.springweb"></context:component-scan>
创建一个类,使用@Componet注解将其注入到Spring容器中
@Component
public class Pojo {
public void fun(){
System.out.println("hello springweb");
}
}
配置监听器用于初始化Spring容器
<!--配置全局监听器用于初始化Spring容器-->
<listener>
<!--ContextLoaderListener是一个全局监听器-->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义全局参数-->
<context-param>
<!--该监听器需要设置要读取的Spring配置文件路径-->
<param-name>contextConfigLocation</param-name>
<!--只是Spring配置文件的路径c1asspath:表示从根目录出发-->
<param-value>classpath:application.xml</param-value>
</context-param>
创建一个servlet,范围跟该servlet,获取spring容器,从容器中获取
@org.springframework.stereotype.Controller
@WebServlet("/hello")
public class MyController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Spring容器
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
//从容器中获取某个bean
Pojo pojo = context.getBean("pojo", Pojo.class);
pojo.fun();
}
}
如何初始化spring容器
实际就是如何在web应用中解析Spring的配置文件
在控制台应用程序中,可以在main方法中通过ClassPathXmlApplicationContext来解析application.xml文件, 初始化Spring容器。
在web项目中没有main方法,只有servlet中的service,如果在service方法中创建ClassPathXmlApplicationContext对象,会每次访问都执行
而Spring容器只需初始化一次,在项目启动时就解析application.xml文件,全局监听器就是一个很好的选择。
spring-web包中提供了一个ContextLoaderListener类, 它实现了ServletContextListener,属于项目级别的全局监听器。
这个类需要一个contextConfigLocation参数,表示要解析的application.xml文件的路径。
这个监听器会在项目启动时,读取指定的Spring配置文件路径,并且创建WebApplicationContext对象,即Spring容器, 这个对象保存在ServletContext中
在spring-web项目中使用Spring-jdbc
1.添加依赖
<dependencies>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--spring容器-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!--web集成-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.23</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
2.创建包结构(controller、servlet、dao、entity),创建实体类
publicclassHero {
privateintid;
privateStringname;
privateStringposition;
privateStringsex;
private intprice;
privateStringshelfDate;
publicHero(intid, Stringname, Stringposition, Stringsex, intprice, StringshelfDate) {
this.id=id;
this.name=name;
this.position=position;
this.sex=sex;
this.price=price;
this.shelfDate=shelfDate;
}
publicHero(Stringname, Stringposition, Stringsex, intprice, StringshelfDate) {
this.name=name;
this.position=position;
this.sex=sex;
this.price=price;
this.shelfDate=shelfDate;
}
publicHero() {
}
//忽略setter/getter/toString方法
}
3.在spring配置文件中配置数据源和JDBC模板
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns="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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描使用了注解的根包-->
<context:component-scanbase-package="com.hqyj.springweb"></context:component-scan>
<!--配置数据库-->
<beanclass="org.springframework.jdbc.datasource.DriverManagerDataSource"id="dataSource">
<!--配置连接驱动-->
<propertyname="driverClassName"value="com.mysql.cj.jdbc.Driver"></property>
<!--配置mysql地址-->
<propertyname="url"value="jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai"></property>
<propertyname="username"value="root"></property>
<propertyname="password"value="123456"></property>
</bean>
<!--配置JDBC模板-->
<beanclass="org.springframework.jdbc.core.JdbcTemplate"id="jdbcTemplate">
<!--设置要操作的数据源-->
<propertyname="dataSource"ref="dataSource"></property>
</bean>
</beans>
4.数据访问层(dao)的写法
@Repository
publicclassHeroDao {
@Autowired
//通过jdbc模板对象实现CRUD
privateJdbcTemplatejdbcTemplate;
publicList<Hero>queryAll() {
Stringsql="select * from hero";
//BeanPropertyRowMapper对象表示将查询到的字段与指定的类中的属性进行映射
BeanPropertyRowMapper<Hero>mapper=newBeanPropertyRowMapper<>(Hero.class);
//query()方法执行查询
List<Hero>list=jdbcTemplate.query(sql, mapper);
returnlist;
}
publicbooleandelete(intid) {
Stringsql="delete from hero where id=?";
inti=jdbcTemplate.update(sql, id);
returni>0;
}
publicbooleaninsert(Herohero){
Stringsql="insert into hero values(null,?,?,?,?,?)";
intinsert=jdbcTemplate.update(sql, hero.getName(), hero.getPosition(), hero.getSex(), hero.getPrice(), hero.getShelfDate());
returninsert>0;
}
publicHerofindById(intid){
Stringsql="select * from hero where id=?";
BeanPropertyRowMapper<Hero>mapper=newBeanPropertyRowMapper<>(Hero.class);
Herohero=jdbcTemplate.queryForObject(sql, mapper, id);
returnhero;
}
publicbooleanupdate(Herohero){
Stringsql="update hero set name=?,position=?,sex=?,price=?,shelf_date=? where id=?";
intupdate=jdbcTemplate.update(sql, hero.getName(), hero.getPosition(), hero.getSex(), hero.getPrice(), hero.getShelfDate(), hero.getId());
returnupdate>0;
}
}
5.控制层(controller)写法
@WebServlet("/hero")
publicclassHeroServletextendsHttpServlet {
@Override
protectedvoidservice(HttpServletRequestreq, HttpServletResponseresp) throwsServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取Spring容器,获取HeroDao对象