Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
◆目的:解决企业应用开发的复杂性
◆创建原因:软件开发的复杂性
IOC(BeanFactory接口)
1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
2、Spring提供IOC容器实现两种方式:(两个接口)
(1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用* 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象(2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用* 加载配置文件时候就会把在配置文件对象进行创建
<bean id=".." class="..."></bean>
<bean id=".." class="...">
<!-- 要事先在实体创建 使用property完成属性注入name:类里面属性名称value:向属性注入的值-->
<property name=".." value=".."></property>
</bean>
第二种注入方式:使用有参数构造进行注入
(1)创建类,定义属性,创建属性对应有参数构造方法
bean id=".." class="...">
<!--要事先在实体类中先创建构造参数-->
<constructor-arg name="" value=""></constructor-arg>
</bean>
字面量
(1)null值
<!--null值-->
<property name="...">
<null/>
</property>
属性值包含特殊符号
<!--属性值包含特殊符号 1 把<>进行转义< > 2 把带特殊符号内容写到CDATA-->
<property name="..">
<value><![CDATA[<<...>>]]>
</value>
</property>
注入属性-外部bean
1)创建两个类service类和dao类
2)在service调用dao里面的方法
3)在spring配置文件中进行配置
public class UserService{ //创建UserDao类型属性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao = userDao;}
public void add() { System.out.println("service add..............."); userDao.update();}} <!--1 service和dao对象创建--> <bean id="userService" class="..."> <!--注入userDao对象name属性:类里面属性名称ref属性:创建userDao对象bean标签id值--> <property name="userDao" ref="userDaoImpl"></property></bean> <bean id="userDaoImpl" class="..."></bean>
注入属性-级联赋值
IOC操作Bean管理(xml注入集合属性)
把集合注入部分提取出来
使用util标签完成list集合注入提取
注入属性-内部bean
1)一对多关系:部门和员工一个部门有多个员工,一个员工属于一个部门部门是一,员工是多
2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {this.dname = dname;}}
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;}
public void setEname(String ename) {
this.ename = ename;}
public void setGender(String gender) {this.gender = gender;}}
(3)在spring配置文件中进行配置<!--内部bean-->
<bean id="emp" class="...Emp">
<!--设置两个普通属性-->
<property name="" value="lucy"></property>
<property name="" value=""></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="...Dept">
<property name="dname" value=""></property>
</bean>
</property>
</bean>
IOC操作Bean管理(FactoryBean)
1、Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)
2、普通bean:在配置文件中定义bean类型就是返回类型
3、工厂bean:在配置文件定义bean类型可以和返回类型不一样
第一步创建类,让这个类作为工厂bean,实现接口FactoryBean
第二步实现接口里面的方法,在实现的方法中定义返回的bean类型
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;}
@Override
public Class<?> getObjectType() {
return null;}
@Override
public boolean isSingleton() {
return false;}}
//定义的是myBean
<bean id="myBean" class="...factorybean.MyBean"></bean>
@Testpublic void test3() {
ApplicationContext context =new ClassPathXmlApplicationContext("bean3.xml");
//返回的是 Course
Course course = context.getBean("myBean", Course.class);System.out.println(course);}
IOC操作Bean管理(bean作用域)
1、在Spring里面,设置创建bean实例是单实例还是多实例
2、在Spring里面,默认情况下,bean是单实例对象
如何设置单实例还是多实例
(1)在spring配置文件bean标签里面有属性(scope)用于设置单实例还是多实例
(2)scope属性值第一个值默认值,singleton,表示是单实例对象第二个值prototype,表示是多实例对象
<bean id=".." class="..." scope="prototype"></bean>
singleton和prototype区别
第一singleton单实例,prototype多实例
第二设置scope值是singleton时候,加载spring配置文件时候就会创建单实例对象
设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象
//无参数构造
public Orders() {
System.out.println("第一步执行无参数构造创建bean实例");}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步调用set方法设置属性值");}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步执行初始化的方法");}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步执行销毁的方法");}}
<bean id="orders" class="....bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
@Test
public void testBean3() {
ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步获取创建bean实例对象");
System.out.println(orders);
//手动让bean实例销毁
context.close();
bean的后置处理器,实现接口BeanPostProcessor,创建后置处理器
bean生命周期有七步
(1)通过构造器创建bean实例(无参数构造)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
(4)调用bean的初始化的方法(需要进行配置初始化的方法)
(5)把bean实例传递bean后置处理器的方法postProcessAfterInitialization
(6)bean可以使用了(对象获取到了)
(7)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
<bean id="emp" class="....autowire.Emp" autowire="byType">
不需要这样赋值:
<bean id=".." class="...">
<!--使用property完成属性注入name:类里面属性名称value:向属性注入的值-->
<property name=".." value=".."></property>
</bean>
<!--引入外部属性文件--><context:property-placeholder location="classpath:jdbc.properties"/>
开启组件扫描细节配置
<!--示例1
use-default-filters="false" 表示现在不使用默认filter,自己配置filtercontext:include-filter ,设置扫描哪些内容-->
<context:component-scan base-package="com...." use-default-filters="false">
<context:include-filter type="annotation"expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例2下面配置扫描包所有内容context:exclude-filter:设置哪些内容不进行扫描-->
<context:component-scan base-package="com...">
<context:exclude-filter type="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan>
基于注解方式实现属性注入
1)@Autowired:根据属性类型进行自动装配
第一步把service和dao对象创建,在service和dao类添加创建对象注解
第二步在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
3)@Resource:可以根据类型注入,可以根据名称注入
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
4)@Value:注入普通类型属性
@Value(value = "abc")
什么是AOP
(1)面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
package com.lvym.spring.dao;
public interface UserDao {
public int add(int a, int b);
public String update(String id);
}
package com.lvym.spring.dao.impl;
import com.lvym.spring.dao.UserDao;
public class UserDaoimpl implements UserDao {
public int add(int a, int b) {
System.out.println("执行add方法----");
return a + b;
}
public String update(String id) {
return id;
}
}
(3)使用Proxy类创建接口代理对象
package com.lvym.spring.proxy;
import com.lvym.spring.dao.UserDao;
import com.lvym.spring.dao.impl.UserDaoimpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JdkProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDaoimpl userDao = new UserDaoimpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
int result = dao.add(1, 2);
System.out.println("result:" + result);
}
}
//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
//1 把创建的是谁的代理对象,把谁传递过来//有参数构造传递 这里表示的是 UserDaoimpl
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法之前执行...." + method.getName() + " :传递的参数..." + Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行...." + obj);
return res;
}
}
第二种没有接口情况,使用CGLIB动态代理
创建子类的代理对象,增强类的方法
Spring框架一般都是基于AspectJ实现AOP操作
(1)AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spirng框架一起使用,进行AOP操作
基于AspectJ实现AOP操作
(1)基于xml配置文件实现
(2)基于注解方式实现(使用)
语法结构:execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
在增强类上面添加注解@Aspect
//前置通知@Before(value = "execution(* ....r.add(..))")
//后置通知(返回通知)@AfterReturning
//最终通知@After
//异常通知@AfterThrowing
//环绕通知@Around
/相同切入点抽取@Pointcut(value = "execution(* ....r.add(..))")
在spring配置文件中开启生成代理对象
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
有多个增强类多同一个方法进行增强,设置增强类优先级(1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Order(1)
public class PersonProxy
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* ...y(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--1 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes></tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* .....*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/></aop:config>
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO"><!--先定义所有的appender-->
<appenders><!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT"><!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} -%msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
Spring5框架核心容器支持 @Nullable注解
(1)@Nullable注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
(2)注解用在方法上面,方法返回值可以为空
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:bean1.xml")
使用一个复合注解替代上面两个注解完成整合
@SpringJUnitConfig(locations = "classpath:bean1.xml")
Spring5框架新功能(Webflux)
(1)是Spring5添加新的模块,用于web开发的,功能和SpringMVC类似的,Webflux使用当前一种比较流程响应式编程出现的框架。
什么是响应式编程
(1) 响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
(2)Java8及其之前版本* 提供的观察者模式两个类Observer和Observable
public class ObserverDemo extends Observable {
public static void main(String[] args) {
ObserverDemo observer = new ObserverDemo();
//添加观察者
observer.addObserver((o,arg)->{System.out.println("发生变化");});
observer.addObserver((o,arg)->{System.out.println("手动被观察者通知,准备改变");});
observer.setChanged();
//数据变化
observer.notifyObservers(); //通知}}
响应式编程(Reactor实现)
(1)响应式编程操作中,Reactor是满足Reactive规范框架
(2)Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现发布者,返回0或者1个元素(3)Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值,错误信号,完成信号,错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.1.5.RELEASE</version>
</dependency>
public static void main(String[] args) {
//just方法直接声明
Flux.just(1,2,3,4);Mono.just(1);
//其他的方法
Integer[] array = {1,2,3,4};
Flux.fromArray(array);
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list);
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);}
(4)三种信号特点
* 错误信号和完成信号都是终止信号,不能共存的
* 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
* 如果没有错误信号,没有完成信号,表示是无限数据流
(5)调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
package com.lvym.springwebflux.server;
import com.lvym.springwebflux.entity.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface UserService {
Mono<User> getUserByid(int id);
Flux<User> getAll();
Mono<Void> insertUser(Mono<User> userMono);
}
package com.lvym.springwebflux.server.impl;
import com.lvym.springwebflux.entity.User;
import com.lvym.springwebflux.server.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
//创建map集合存储数据
private final Map<Integer,User> users = new HashMap<>();
public UserServiceImpl(){
this.users.put(1,new User("lucy","男",20));
this.users.put(2,new User("mary","女",30));
this.users.put(3,new User("jack","女",50));
}
/**
* 根据id查询
* @param id
* @return
*/
@Override
public Mono<User> getUserByid(int id) {
return Mono.justOrEmpty(this.users.get(id));
}
/**
* 查询所有用户
* @return
*/
@Override
public Flux<User> getAll() {
return Flux.fromIterable(this.users.values());
}
/**
* 添加
* @param userMono
* @return
*/
@Override
public Mono<Void> insertUser(Mono<User> userMono) {
//先把值取出来再放回map
return userMono.doOnNext(user -> {
int id = users.size() + 1;
this.users.put(id,user);
}).thenEmpty(Mono.empty());
}
}
package com.lvym.springwebflux.controller;
import com.lvym.springwebflux.entity.User;
import com.lvym.springwebflux.server.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUserByid/{id}")
public Mono<User> getUserByid(@PathVariable int id){
return userService.getUserByid(id);
}
@GetMapping("/getAll")
public Flux<User> getAll(){
return userService.getAll();
}
@PostMapping("/insertUser")
public Mono<Void> insertUser(@RequestBody User user){
Mono<User> userMono = Mono.just(user);
return userService.insertUser(userMono);
}
}