Spring
一、spring核心内容
1、IOC
IOC:控制反转。控制权反转给spring。
之前对象的创建是由程序员完成的,用了spring之后,对象的创建教给了spring容器。spring可以帮助我们管理bean对象。
如何使用spring
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.5</version> </dependency>
创建beans.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="users" class="com.openlab.beans.Users"></bean> </beans>
使用
public void test02(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Object obj = applicationContext.getBean("users");//通过id获取 System.out.println(obj); Users users = applicationContext.getBean("users",Users.class);//通过id和类型 System.out.println(users); System.out.println(users.getUsername()+"\t"+users.getPassword()+"\t"+users.getStatus()); Users users01 = applicationContext.getBean(Users.class);//通过类型获取,要求spring容器中只能有唯一的改类型的bean对象 System.out.println(users01); }
spring给对象的属性赋值
1、set注入:直接调用set方法
2、构造子注入:
<bean id="users01" class="com.openlab.beans.Users"> <constructor-arg value="1" ></constructor-arg> <constructor-arg value="2"></constructor-arg> </bean> <bean id="users01" class="com.openlab.beans.Users"> <constructor-arg value="1" name="status" ></constructor-arg> <constructor-arg value="2" name="password"></constructor-arg> </bean> <!-- 改类中有两个参数的构造方法 --> <!--调用有一个参数的构造方法,如果有多个一个参数的构造方法,咱们给status属性的构造方法执行--> <bean id="users01" class="com.openlab.beans.Users"> <constructor-arg name="status" value="1"></constructor-arg> <property name="username" value="test01"></property> </bean> <!-- ref:指向spring容器中的另一个bean对象 -->
3、接口注入(DI)
DI:依赖注入,service依赖dao.将dao的创建给了spring容器。
@Getter @Setter public class UsersServiceImpl { private UsersDao usersDao ; public void save(){ Users users = new Users(); usersDao.save(users); System.out.println("---service中的添加方法---"); } }
<bean id="userDaoImpl1" class="com.openlab.dao.impl.UsersDaoImpl"></bean> <bean id="userDaoImpl2" class="com.openlab.dao.impl.UsersDaoImpl2"></bean> <bean id="userService" class="com.openlab.service.UsersServiceImpl"> <property name="usersDao" ref="userDaoImpl2"></property> </bean>
4、内部bean
<bean id="emp3" class="com.openlab.beans.Emp"> <property name="empid" value="1003"></property> <property name="empname" value="juddy"></property> <property name="dept"> <!--内部bean的定义,使用只能再给emp3中的dept属性赋值 可以给id属性赋值,该bean再其他的地方也是不能使用的。 --> <bean class="com.openlab.beans.Dept" id="dept"> <property name="deptname" value="测试部"></property> </bean> </property> </bean>
5、集合属性赋值
@Getter @Setter @NoArgsConstructor public class CollectionTest { private String[] arr; private List<String> list; private Map<String,String> map; private Set<String> set; private List<Emp> emps; private Map<String,Emp> empMap; }
<bean id="collectionTest" class="com.openlab.beans.CollectionTest"> <property name="arr"> <array> <value>arr01</value> <value>arr02</value> <value>arr03</value> </array> </property> <property name="list"> <list> <value>list01</value> <value>list02</value> <value>list03</value> </list> </property> <property name="map"> <map> <entry key="zh" value="中国"></entry> <entry key="jp" value="日本"></entry> <entry key="us" value="美国"></entry> </map> </property> <property name="set"> <set> <value>set01</value> <value>set02</value> <value>set03</value> </set> </property> <property name="emps" ref="emplist"> <!-- <list> <ref bean="emp"></ref> <ref bean="emp2"></ref> <ref bean="emp3"></ref> </list>--> </property> <property name="empMap"> <map> <entry key="1001" value-ref="emp"></entry> <entry key="1002" value-ref="emp2"></entry> <entry key="1003" value-ref="emp3"></entry> </map> </property> </bean> <bean class="com.openlab.beans.Emp" id="emp"> <property name="empid" value="1001"></property> <property name="empname" value="jerry"></property> </bean> <bean class="com.openlab.beans.Emp" id="emp2"> <property name="empid" value="1002"></property> <property name="empname" value="lucy"></property> </bean> <bean id="emp3" class="com.openlab.beans.Emp"> <property name="empid" value="1003"></property> <property name="empname" value="juddy"></property> <property name="dept"> <!--内部bean的定义,使用只能再给emp3中的dept属性赋值--> <bean class="com.openlab.beans.Dept"> <property name="deptname" value="测试部"></property> </bean> </property> </bean> <util:list id="emplist"> <ref bean="emp"/> <ref bean="emp2"/> <ref bean="emp3"/> </util:list>
如果使用util:list必须加入头部信息
<?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" xmlns:c="http://www.springframework.org/schema/c" xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" 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-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"> </beans>
6、Bean工厂
public class MyFactoryBean implements FactoryBean<Emp> { @Override public Emp getObject() throws Exception { Emp emp = new Emp(); emp.setEmpid(1001); emp.setEmpname("工厂bean创建的bean"); return emp; } @Override public Class<?> getObjectType() { return Emp.class; } }
<bean id="myFactoryBean" class="com.openlab.beans.MyFactoryBean"></bean>
@Test public void test16(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans06.xml"); MyFactoryBean factoryBean = context.getBean(MyFactoryBean.class); System.out.println(factoryBean); Emp emp = context.getBean("myFactoryBean",Emp.class);//获取员工的信息 System.out.println(emp); }
7、Bean的作用域[是否是单例]
<!-- prototype:非单例模式 prototype:单例模式 默认值 --> <bean id="users" class="com.openlab.beans.Users" scope="prototype|singleton"> <property name="username" value="admin"></property> <property name="password" value="admin123"></property> <property name="status" value="1"></property> <property name="usersInfo" ref="userinfo"></property> </bean>
8、Bean的生命周期
1>构造
2>set
3>初始化
4>bean可以正常使用
5>销毁
<!-- init-method:初始化方法 destroy-method:销毁的方法 --> <bean id="beanLife" class="com.openlab.beans.BeanLife" init-method="init" destroy-method="destroy"> <property name="name" value="生命周期"></property> </bean>
@Test public void test17(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans06.xml"); BeanLife emp = context.getBean(BeanLife.class); System.out.println(emp); context.close();//context进行close 的时候,才会执行destroy方法 }
bean的生命周期 七部
1>构造
2>set
3>初始化方法之前
4>初始化方法
5>初始化方法之后
6>bean可以正常使用
7>销毁
使用步骤 1>定义BeanPostProcessor类
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化前"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("初始化后"); return bean; } }
2>将以上定义的类交给spring管理
<bean id="myBeanPostProcessor" class="com.openlab.beans.MyBeanPostProcessor"></bean> <bean id="beanLife" class="com.openlab.beans.BeanLife" init-method="init" destroy-method="destroy"> <property name="name" value="生命周期"></property> </bean>
3>测试 加载以上beans.xml文件的时候,里面所有的bean都会执行定义的生命周期中的方法
@Test public void test17(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans06.xml"); BeanLife emp = context.getBean(BeanLife.class); System.out.println(emp); context.close(); }
9、自动装配
<bean id="userDaoImpl1" class="com.openlab.dao.impl.UsersDaoImpl"></bean> <bean id="userService" class="com.openlab.service.UsersServiceImpl" autowire="byType"></bean> <!-- autowire="byName" 可以定义多个dao对象,会将id和属性名相同的 对象赋值给属性 autowire="byType" 只能定义一个dao对象,将该dao实例赋值给属性 -->
10、引入资源文件
druid.properties
druid.driverClassName:com.mysql.cj.jdbc.Driver druid.url:jdbc:mysql:///test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true druid.name:root druid.password:123456
<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" xmlns:c="http://www.springframework.org/schema/c" xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" 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-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"> <context:property-placeholder location="classpath:druid.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${druid.name}"></property> <property name="url" value="${druid.url}"></property> <property name="password" value="${druid.password}"></property> <property name="driverClassName" value="${druid.driverClassName}"></property> </bean> </beans>
练习:
1>整理笔记,知识点整理出来,知识点对应的练习【博客】
11、注解的使用
1>xml文件中添加要扫描的基本包
<context:component-scan base-package="org.openlab"/>
2>类中添加注解,代表该类已经被spring管理
@Component//普通的java类 @Repository//持久层,dao层 @Service//业务层 service层 @Controller //控制层 servlet,mvc
spring管理bean的时候,默认给bean起别名,别名教 类名首字母小写。
通过以上注解的value属性给对象起别名
@Getter @Setter @Component(value = "userinfo")//不使用value属性,该类的对象名教users。如果使用value属性就是给类起新的名字:userinfo public class Users { private Integer userid; private String username; private String password; }
<context:component-scan base-package="org.openlab"> <!--不包含Controller注解,spring会管理基础包下的所有的类,但是不包含Controller注解--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<context:component-scan base-package="org.openlab" use-default-filters="false"> <!--只包含Controller注解,必须不能使用默认的filter,use-default-filters="false"--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
自动注入,action依赖service.在action中直接通过@Autowired注解给属性注入值
@Autowired
@Controller public class UsersAction { @Autowired private UsersService usersService; public void save(){ Users users = new Users(); usersService.save(users); } }
如果一个接口对应多个实现类的时候,此时使用@Autowired不知道将哪个实现类注入进进去,此时借助@Qualifier 来指定注入的是哪个对象
@Service public class UsersServiceImpl implements UsersService { @Qualifier(value = "usersDaoImpl2")//指定拿一个实现类注入信息的 @Autowired private UsersDao usersDao ; @Override public void save(Users users) { System.out.println("service层中的save方法执行了"); usersDao.save(users); } }
@Resource :是将name指定的对象注入到属性中。等价与@Autowired与@Qualifier综合
/* @Qualifier(value = "usersDaoImpl2")//指定拿一个实现类注入信息的 @Autowired*/ @Resource(name = "usersDaoImpl2") private UsersDao usersDao ;
@Value 注入值
@Getter @Setter @Component public class Users { @Value("1001") private Integer userid; @Value("admin") private String username; @Value("123456") private String password; }
12、完全注解,不依赖配置文件
1>普通的java类充当配置信息
@Configuration @ComponentScan(value = {"org.openlab"}) public class SpringConfig { }
2>测试,创建的对象是 AnnotationConfigApplicationContext
@Test public void test01(){ ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UsersAction usersAction = context.getBean(UsersAction.class); usersAction.save(); }
作业> spring管理bean对象
1>创建类
public class Car { private String brand; private String corp; private double price; private int maxpeed; public Car(String brand, String corp, double price) { super(); this.brand = brand; this.corp = corp; this.price = price; } public Car(String brand, String corp, int maxpeed) { super(); this.brand = brand; this.corp = corp; this.maxpeed = maxpeed; } @Override public String toString() { return "Car [brand=" + brand + ", corp=" + corp + ", price=" + price + ", maxpeed=" + maxpeed + "]"; } }
将该类教给spring管理,同时
1) 借助set给属性注入值
2)借助构造方法给属性注入值。【两个都是三个参数的构造方法】
2>创建类,包含集合属性,如何给以下集合属性注入信息
public class CollectionTest { private String[] arr; private List<String> list; private Map<String,String> map; private Set<String> set; private List<Emp> emps; private Map<String,Emp> empMap; }
3>创建部门和员工类
public class Dept{ private Integer deptid; private String deptname; private String deptdesc; private List<Emp> list; } public class Emp{ private Integer empid; private String empname; private String empsex; private Dept dept; }
将类交给spring管理,给dept中的list集合赋值.一种list赋值。util:list
emp:给emp中的dept赋值
4>注解的练习:【熟练掌握】
创建Service以及Dao service依赖dao 。自动注入dao.
dao又多了一个实现类,如何注入?
5>纯注解,不需要配置文件
2、AOP
OOP:面向对象
AOP:面向切面编程
实现:
1>普通的类,教给spring管理
@Component public class UsersDaoImpl { public void save(){ System.out.println("userDao中实现类1的save"); } public Integer update(Users user) { System.out.println("dao实现类1执行了。。。"+user); return 10; } public Integer test(int a, int b) { System.out.println("dao实现类1执行了。。。。"); return a+b; } }
2>定义aspect类, 该类也是需要教给spring管理的,同时是要给@Aspect类
@Component @Aspect// AopPoxy是一个切面类,需要交给spring管理 public class AopProxy { //切入点:pointcut //before:通知的类型 前置通知 @Before("execution(* com.openlab.aop.UsersDaoImpl.save())")//* com.openlab.aop.UsersDaoImpl.save():连接点 public void beforeMethod(){ System.out.println("在方法执行之前要执行的代码"); } }
该类是要给切面类,称为aspect类
需要对哪个方法进行增强,需要对UsersDaoImpl.save() 方法做增强,该方法就称为链接点
需要对save方法增强的方法,成功称为切入点。
在save方法什么时机增强,通知的类型,前置通知,后置通知,环绕通知,最终通知,异常通知
环绕方法之前 @Around
前置通知 @Before
最终通知 @AfterReturning|异常通知@AfterThrowing
后置通知@After
环绕方法之后@Around
@Component @Aspect// AopPoxy是一个切面类,需要交给spring管理 @Order(0) public class AopProxy02 { @Pointcut("execution(* com.openlab.aop.*.*.*(..))") public void mypoint(){ } //切入点:pointcut //before:通知的类型 前置通知 @Before(value = "execution(* com.openlab.aop.*.*.*(..))")//* com.openlab.aop.impl1.UsersDaoImpl.save():连接点 public void beforeMethod(){ System.out.println("第0个代理对象中的前置通知::在方法执行之前要执行的代码"); } @After(value = "execution(* com.openlab.aop.*.*.*(..))") public void afterMethod(){ System.out.println("后置通知:方法之前之后要执行的代码"); } @AfterReturning(value = "execution(* com.openlab.aop.*.*.*(..))") public void returnMethod(){ System.out.println("最终通知:"); } @AfterThrowing(value = "execution(* com.openlab.aop.*.*.*(..))") public void exectionMethod(){ System.out.println("异常通知:"); } @Around(value = "execution(* com.openlab.aop.*.*.*(..))") public void roundMethod(ProceedingJoinPoint joinPoint){ System.out.println("环绕方法之前"); try { joinPoint.proceed();//继续执行你的方法 } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("环绕方法之后"); } }
可以抽取pointcut():切入点
@Component @Aspect// AopPoxy是一个切面类,需要交给spring管理 @Order(0)//如果有多个代理的时候,哪个先执行,可以通过order定义,数字越小越先执行 public class AopProxy02 { @Pointcut("execution(* com.openlab.aop.*.*.*(..))") public void mypoint(){ } //切入点:pointcut //before:通知的类型 前置通知 @Before(value = "mypoint()")//* com.openlab.aop.impl1.UsersDaoImpl.save():连接点 public void beforeMethod(){ System.out.println("第0个代理对象中的前置通知::在方法执行之前要执行的代码"); } @After(value = "mypoint()") public void afterMethod(){ System.out.println("后置通知:方法之前之后要执行的代码"); } @AfterReturning(value = "mypoint()") public void returnMethod(){ System.out.println("最终通知:"); } @AfterThrowing(value = "mypoint()") public void exectionMethod(){ System.out.println("异常通知:"); } @Around(value = "mypoint()") public void roundMethod(ProceedingJoinPoint joinPoint){ System.out.println("环绕方法之前"); try { joinPoint.proceed();//继续执行你的方法 } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("环绕方法之后"); } }
注解的方式
1>定义两个类
public class AopProxy { public void beforeMehtod(){ System.out.println("方法执行之前"); } public void afterMehtod(){ System.out.println("方法执行之后"); } public void afterRuturning(){ System.out.println("最终通知,返回通知"); } }
public class EmpDaoImp { public void save(String name){ System.out.println("empDao类中的save方法"); if(name.equals("error")){ throw new RuntimeException("名字含有特殊字符"); } } }
通过配置的方式实现aop
<bean id="empDao" class="com.openlab.annocation.EmpDaoImp"></bean> <bean id="aopProxy" class="com.openlab.annocation.AopProxy"></bean> <aop:config> <aop:pointcut id="mypoint2" expression="execution(* com.openlab.annocation.*.*(..))"/><!--pointcut:切入点--> <aop:aspect ref="aopProxy"> <!--切面类--> <aop:before method="beforeMehtod" pointcut-ref="mypoint2"></aop:before> <aop:after method="afterMehtod" pointcut-ref="mypoint2"></aop:after> <aop:after-returning method="afterRuturning" pointcut-ref="mypoint2"></aop:after-returning> </aop:aspect> </aop:config>