框架
分类
- Spring
整个框架中负责宏观调控的,整合其他第三方的框架。 - SpringMVC
主要负责实现前后端数据的交互 - Mybatis/MybatisPlus
持久层框架,简化了JDBC操作数据库的方式,提高效率 - SpringBoot框架/工具
采用了一种更加简化的方式封装了之前的框架,让程序变得更加简单。
分层
Java打包类型
- Jar包文件 适用于工具API、框架源码、SpringBoot程序,也是最常用的一种方式
- War包文件 动态Web开发时常用的方式,可以直接tomcat服务器部署,目后已有些落后。
- pom类型 表示该项目是一个聚合的工程
Spring 框架(必会内容)
介绍
针对bean的生命周期进行管理的轻量级容器(LightWeight container),提供了IOC\AOP,主要学习其思想,及使用方式。由Rod Johnson
面向接口
变与不变的部分,抽象
面向抽象编程的目的是为了应对用户需求的变化,将某个类中经常因需求变化而需要改变的代码从该类中分离出去。面向抽象编程的核心是让类中每种可能的变化对应地交给抽象类的一个子类去负责,从而让该类的设计者不去关心具体实现,避免所设计的类依赖于具体的实现。面向抽象编程使设计的类容易应对用户需求的变化。参考连接
控制反转
IOC全称Inversion of Control 即“控制反转”,是一种设计思想,对象创建的权利由Spring框架 完成,由容器完成对象生命周期的管理
实现方式1:使用xml文件
在resource
目录下创建spring.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">
<!--
知识点讲解:
1.bean 被spring容器管理的对象称之为bean
2.id 全局唯一变量一般类型首字母小写
3.class class的路径信息,需要写全名
-->
<bean id="user" class="com.jt.User">
<!-- 构造函数的注入 -->
<constructor-arg name="name" value="张三"></constructor-arg>
<!-- 数组的注入 -->
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>三国演义</value>
</array>
</property>
<!-- map的注入 -->
<property name="cards">
<map>
<entry key="身份证" value="3712616516516515491351"></entry>
<entry key="招行卡" value="62208561561561601651"></entry>
</map>
</property>
<!-- list的注入 -->
<property name="games">
<list>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</list>
</property>
</bean>
</beans>
然后在类里面先创建ApplicationContext
对象,传入上面编写的xml
文件,则此上下文就可以帮我们管理在xml
里面定义的类了
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
可以通过以下两种方式获得xml里面定义的对象
- 方式1:由于传入的是字符串,所以返回值的类型是一个Object,因此需要强转
User user1 = (User)ctx.getBean("user");
- 方式2:由于传入的是字节码对象,因此可以通过反射直接返回该类型的对象
User user2 = ctx.getBean(User.class);
- 解释
Spring容器的数据结构大概就是一个Map集合,key=xml里的id,value=通过反射实例化出来的对象
//Spring容器里面的getBean()方法的源码大致就像这个样子
//通过类路径来获取对象
public static Object getObject(String classPath) {
try {
return Class.forName(classPath).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//通过类类型来获取对象
public static <T> T getObject(Class<T> tClass) {
try {
return tClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
实现方式2:使用配置类加注解的方式
使用配置类的形式,现在使用配置类加注解的形式
首先在配置类的类名上方添加@Configuration
注解,声明此类为一个配置类
@Bean
用于配置类里面,作用在方法上,方法名就是beanId,返回值就是后面要注入的对象,所以要求方法必须要有返回值。此时,Spring容器里面的那个类似Map的结构大致就变成了这样:Map<方法名,方法的返回值>
import cn.tedu.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration//声明此类是一个配置类
public class PersonSpringConfig {
@Bean//标识此bean,id为user,value为方法的返回值
public User user() {
return new User();
}
}
- 获取对象时使用容器对象换成了
AnnotationConfigApplicationContext
,其他的方式都一样
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(PersonSpringConfig.class);
User user = (User) ctx.getBean("user");
user.say();
}
Spring创建对象–工厂模式(必会内容)
@Component
注解:作用于类上,表示此类会被Spring容器自动创建对象,并添加到容器,key是类名的首字母小写@ComponentScan("cn.tedu")
注解:与@Configuration
注解一起使用,用于指明该配置类需要扫描的包路径,也会扫描其子孙包
package cn.tedu.entity;
import org.springframework.stereotype.Component;
@Component //交由容器自动创建对象,beanId=类名首字母小写
public class UserManager implements Person {
public void say(){System.out.println("user.say()");}
}
在配置类中添加包扫描
package cn.tedu.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("cn.tedu")//添加包扫描路径,也会扫描其子孙包。使用此配置类时会启动此扫描
public class PersonSpringConfig {}
测试类中
ApplicationContext ctx = new AnnotationConfigApplicationContext(PersonSpringConfig.class);
UserManager user = (UserManager) ctx.getBean("userManager");
user.say();
Spring 工厂
- 创建工厂类,并实现
FactoryBean<T>
这个接口,注意把泛型直接声明好,比如这里的Calendar
- 实现该接口下的
T getObject()
和Clcass<?> getObjectType()
两个方法。T getObject()
此方法为使用Spring容器获取对象时,容器会返回此方法的返回值。Clcass<?> getObjectType()
方法用于当使用类类型来获取对象时判断类型使用。 - 对类施加
@Component
注解。BeanID默认是工厂类的首字母小写,也可以在注解里面指定BeanID@Component("calendar")
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;
import java.util.Calendar;
@Component("calendar")//这里可以指定BeanID,如果不指定的话,默认就是工厂类名的首字母小写
public class CalendarFactory implements FactoryBean<Calendar> {
// 获取对象时容器会自动调用此方法来得到返回值,并作为Value返回给调用者
@Override
public Calendar getObject() throws Exception {
return Calendar.getInstance();
}
// 使用类类型获取对象时,会自动调用此方法来做类型匹配
@Override
public Class<?> getObjectType() {
return Calendar.class;
}
}
Bean 作用域
@Scope
注解
通常与@Bean
注解一起使用
@Scope(“singleton”) //默认值 单例模式
@Scope(“prototype”)//默认值 多例模式
范围 | 描述 |
---|---|
singleton | (默认)将单个 bean 定义范围限定为每个 Spring IoC 容器的单个对象实例。 |
prototype | 将单个 bean 定义范围限定为任意数量的对象实例。 |
request | 将单个 bean 定义范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该 bean 实例是在单个 bean 定义的后面创建的。仅在 web-aware Spring 的上下文中有效ApplicationContext。 |
session | 将单个 bean 定义范围限定为 HTTP 的生命周期Session。仅在 web-aware Spring 的上下文中有效ApplicationContext。 |
application | 将单个 bean 定义范围限定为ServletContext. 仅在 web-aware Spring 的上下文中有效ApplicationContext。 |
websocket | 将单个 bean 定义范围限定为WebSocket. 仅在 web-aware Spring 的上下文中有效ApplicationContext。 |
package com.jt.config;
import com.jt.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
@ComponentScan("com.jt")
public class SpringConfig {
@Bean
//@Scope("singleton") //默认值 单例模式
@Scope("prototype") //默认值 多例模式
public User user(){
return new User();
}
}
懒加载机制
@Lazy
注解用于标识该Bean延迟到使用时才加载(类比单例模式中的懒汉式)
仅用于单例模式下:单例模式下所有的bean是初始化容器时直接就加载了,使用了@Lazy
注解后,对应的bean会延迟到使用时才会加载。此模式下实例出来的对象的生命周期会一直由容器管理。
多例模式下,默认所有的bean都是懒加载的,且每次使用都会创建新的对象,此种模式下容器在创建对象、初始化然后把对象交出来后,对象的生命周期就由客户自己管理了,容器不再持有对象的管理。
Spring生命周期管理
@PostConstruct
注解标识的方法会在对象被创建后(即构造函数执行后)被自动调用
@PreDestroy
注解标识的方法会在销毁对象之前被自动调用。一般是在调用AnnotationConfigApplicationContext.close()
和ClassPathXmlApplicationContext.close()
方法,对容器进行关闭操作时,会自动调用由此注解标识的方法
注意:多例模式下,容器在创建、初始化完成,将对象交给客户后,也同时交出了对象的生命周期的管理,因此在多例模式下,关闭容器时不会调用
@PreDestroy
注解标识的方法
Dependenty Injection
@Autowired
注解
说明:在对象中如果需要使用属性注入,一般使用@Autowired注解
功能,可 以将Spring容器中的对象,自动注入到属性中。
优先按byType来匹配,如果要被注入的是接口则自动注入的是其子实现类。如果子实现类有多个,会先按byName来匹配,如果也匹配不到。则会报错UnsatisfiedDependencyException
@Qualifier
如果一个类型有多个子实现类,需要手动指定要注入哪一个实现类时,可以使用此注解来指定
Properties配置文件
在src/main/resources
文件夹下创建user.properties
文件
# 使用 key=value 的形式来声明,中间不要添加多余的空格
# 说明:windows系统中有环境变量username=系统用户名称
# 所以后面写业务数据,最好不要使用username关键字
user.username=张三
然后在需要此配置文件的类名上端加如下注解
// 此处的classpath:/ 就代表resource目录
@PropertySource("classpath:/user.properties")
如果出现乱码,需要指定字符集
// 指定文件的字符集,默认使用ISO-8859-1字符集
@PropertySource(value="classpath:/user.properties",encoding = "utf-8")
向属性注入时使用Spring的EL表达式${key}
传送门:4. Spring Expression Language (SpEL)https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
@Value("${user.username}")
private String name;
@Value("#{'${user.hobbys}'.split(',')}")
private List<String> list;
- 综合代码
package pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
public class MySpringTest {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User) ctx.getBean("user");
user.playWithPet();
}
}
@Configuration
@ComponentScan("pojo")
class MyConfig{
}
@Component
class User{
@Autowired
@Qualifier("cat")
private Pet pet;
public void playWithPet(){
System.out.println("user.play()");
this.pet.play();
}
}
interface Pet{ void play();}
@Component
class Cat implements Pet{
@Value("花花")
private String name;
@Override
public void play() {
System.out.println("小猫"+name+"在玩");
}
}
@Component
@PropertySource(value = "classpath:/set.properties" , encoding = "UTF-8")
class Dog implements Pet{
@Value("#{'${dog.hobbys}'.split(',')}")
private List<String> hobbys;
private String name;
@Override
public void play() {
System.out.println("小狗"+name+"在玩");
}
@Value("大黄")
public void setName(String name) {
this.name = name;
}
}
MVC
设计思想
Model 指业务层,进行业务相关处理
View 指用户界面,用于数据展现
Controller指控制层,用于总体流程的控制
小结:MVC是一种设计思想,在编程中降低代码的耦合性。但是根据实际的开发情况,很多的业务逻辑比较复杂,如果后端将所有的代码都写到同一个Java类中,这样代码结构很臃肿,为了很好的实现MVC设计思想,所以后端代码也应该分层。 三层结构是MVC思想的一种体现
分层说明:
层 | 单词 | 注解 | 说明 |
---|---|---|---|
控制层 | Controller | @Controller | 与前端页面交互的 |
业务层 | Service | @Service | 处理业务逻辑 |
持久层 | Mapper | @Repository | 实现数据库的相关操作 |
@Repository
注解等同于@Component
注解,此注解的目的只是让开发者有层级的概念
一层需要调用另外一层的,可以让其持有另一层的接口,对象使用DI注入,如果当前层不会被另一层调用,则封装接口不是必须的。比如Controller就不需要再封装接口。
package cn.tedu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
public class MySpringTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
UserController userController = context.getBean("userController", UserController.class);
userController.insert();
}
}
interface UserMapper{
void addUser();
}
@Repository
class UserMapperImpl implements UserMapper{
@Override
public void addUser() {
System.out.println("新增用户");
}
}
interface UserService{
void insertUser();
}
@Service
class ServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
public void insertUser() {
System.out.println("insert user");
userMapper.addUser();
}
}
@Controller
class UserController {
@Autowired
UserService userService;
public void insert(){
userService.insertUser();
}
}
@Configuration
@ComponentScan("cn.tedu")
class MySpringConfig{
}
IOC/DI小结
- 历史:传统代码中对象的创建都是使用
new
关键字手动创建,这样的代码耦合性高,不方便扩展。 - 功能:
- IOC:由Spring容器管理对象的生命周期。
- 使得对象与对象之间的耦合性降低
- DI是依赖注入,只有被Spring字符串管理的对象才可以被依赖注入默认的条件下采用类型注入,如果有特殊需求也可以采用名称注入。
- Spring中的IOC和DI相互配合,可以极大程度上降低耦合性。
- 作用:Spring由于采用了IOC和DI的设计方式,可以整合其它的第三方框架,使得程序的调用浑然一体。
Spring小结
- 为什么学习
- 为什么面向接口编程
以后对象中属性一般写接口,Java中金矿的 - 什么是IOC
- Spring容器启动方式
xml和annotation - 什么时候使用工厂模式
对象无法直接实例化的时候、Spring整合第三方框架的时候FactoryBean<>
@Scope("singleton")
@Scope("prototype")
@lazy
只对单例模式有效- 生命周期:construct、
@PostConstruct
、invoke、@PreDestory
- DI (Dependency Injection)
@Autowired
byType~byName(@Qualifier(beanId)
)@Resource()
@Vaule
- MVC设计思想:视图层、业务层、控制层
- Controller\Service\Maper|Dao
@Value
为基本类型和String的属性赋值- 动态代理:
JDK
Proxy.newProxyInstance(classLoader,interfaces,InvocationHandler)
CGLIB
Enhance.setSuperClass().setCallback().create()