spring配外部文件
我们先在Spring里配置一个数据源
1.导c3p0包,这里我们先学一下hibernate持久化框架,以后用mybites.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.17.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>5.3.0.Final</version>
</dependency>
2.安装mysql 8.x数据库(参考相关资料),导入驱动包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
3.看具体代码:
<!-- 配置c3p0,ComboPooledDataSource对象,它指向一个数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="xiong"></property>
<property name="jdbcUrl" value="jdbc:mysql://124.220.60.104:3306/spring5"></property>
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
</bean>
测试数据库连接
@Test
public void dataSourcetest() throws SQLException{
ApplicationContext ct = new ClassPathXmlApplicationContext("dataSource.xml");
DataSource dataSource = (DataSource)ct.getBean("dataSource");
System.out.println(dataSource.getConnection());
}
-----------------------------------------------------------------
com.mchange.v2.c3p0.impl.NewProxyConnection@45c7e403 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@2925bf5b] //连接成功,产生一个代理数据库服务器
上面这样配置,写死了数据是相对以后转生产环境中修改是十分不方便的,生产环境中,我们极有可能会把dao层打jar包,又分成多模块开发,所以修改起来是十分麻烦,所以我们会把值写成键值对的形式单独保存到一个文件中
使用外部文件保存数据值,再配spring配置文件
jdbc.properties
jdbc.user=root
jdbc.password=xiong
jdbc.url=jdbc:mysql://124.220.60.104:3306/spring5
jdbc.driverClass=com.mysql.cj.jdbc.Driver
dataSouce.xml–spring配置文件
<!-- 配置c3p0,ComboPooledDataSource对象,它指向一个数据源 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
</bean>
Spring的表达式语言spEL
Spring表达式语言(SpEL):是一个支持运行时查询和操作对象图的强大表示是语言,是一种可以与一个基于spring的应用程序中的运行时对象交互的东西。总得来说SpEL表达式是一种简化开发的表达式,通过使用表达式来简化开发,减少一些逻辑、配置的编写。
语法类似于 EL:SpEL 使用 #{…} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。
通过 SpEL 可以实现:
通过 bean 的 id 对 bean 进行引用,用了SpEL在bean标签中可以用value代替ref。
可以像EL一样用点运算符调用方法以及对象中的属性。
计算表达式的值
正则表达式的匹配。
SpEL 字面量,意义不大,spring内部本身有数据类型的自动转换机制,直接写值就好了,不必用SqEL,了解:
整数:#{8} 实际上,直接写成“8”即可,如前面讲的:p:id="1",就表示1
小数:#{8.8}
科学计数法:#{1e4}
String:可以使用单引号或者双引号作为字符串的定界符号。
Boolean:#{true}
SpEL引用bean , 调用它属性和方法:
引用其他对象:#{car}
引用其他对象的属性:#{car.price}
调用其它方法 , 还可以链式操作:#{person.pet.toString()}
调用静态方法静态属性:#{T(java.lang.Math).PI}
Spring EL 操作List、Map集合取值
//SpEL支持的运算符号:
算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
逻辑运算符:and , or , not , |
if-else 运算符(类似三目运算符):?:(temary), ?:(Elvis)
正则表达式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}
例:引用对象举例
为了测试,我在Pet类中,添加成员属性private double price
及get/set方法,在Person类中添属性成员private double petPrice,private double pi
及get/set方法,petPrice属性在spring创建对象时,引用Pet对象中定义的值,pi属性引用lang包中的静态成员常量。
<!-- springEL,测试 -->
<bean id="cat1" class="cn.ybzy.springdemo.model.Pet" p:id="1" p:type="cat" p:price="1000.0"></bean>
<bean id="person" class="cn.ybzy.springdemo.model.Person">
<property name="id" value="1"></property>
<property name="pet" value="#{cat1}"></property>
<property name="petPrice" value="#{cat1.price}"></property>
<property name="pi" value="#{T(java.lang.Math).PI}"></property>
</bean>
/*
* spring EL 测试
*/
@Test
public void springEltest() {
ApplicationContext ct = new ClassPathXmlApplicationContext("springEL.xml");
Person person=(Person)ct.getBean("person");
System.out.println(person);
System.out.println(person.getPi());
}
--------------------------------------------------------------------
Person [id=1, pet=Pet [id=1, type=cat, price=1000.0], cars=null, petPrice=1000.0]
3.141592653589793
例:springEL运算符演示,这里我只举,三目运算会和正则表达式匹配运算
为了测试,我在Person类中添加test属性,private String test
及get/set方法,它会根根价格来作两个判 断,大于800元,是土豪,小于100元是普通人养宠物。
<property name="test" value="#{cat1.price > 800 ? '土豪' : '普通人'}"></property>
System.out.println(person.getTest());
---------------------------------------------------------------------
土豪
正则表达式测试,字符串 ''aaaa98734"不全是数字,所以匹配0-9多个数字是错的
<property name="test" value="#{'aaa98734' matches '[0-9]+'}"></property>
System.out.println(person.getTest());
---------------------------------------------------------------------
false
例:引用List,Map
map[‘1’]中的1是KEY
Spring通过工厂方法进行配置
在Spring的世界中, 我们通常会利用 xml配置文件 或者 annotation注解方式来配置bean实例!
在第一种利用 xml配置文件 方式中, 还包括如下三小类
- 反射模式(我们前面的所有配置都是这种模式)
- 工厂方法模式
- Factory Bean模式
工厂方法进行配置
静态工厂方法方式是非常适用于作为1个bean容器, 只不过bean集合定义在工厂类里面而不是项目xml配置文件里面。
缺点也比较明显, 把数据写在class里面而不是配置文件中违反了我们程序猿的常识和spring的初衷。当然优点就是令人恶心的xml配置文件更加简洁。所以,工厂方法的配置,了解一下就行了,个人建议不要在项目中使用,但要了解,以后看到项目中有这样的方式,能看得懂。
FactoryBean 来配置Bean
spring通过FactoryBean配置,比前面的工厂方法配置Bean要重要些,因为我们整合很多第三方的框架的时候,需要用到FactoryBean来配置第三方框架中的bean 对象,从而把第三方框架整合到spring中来!当然在整合这些第三方框架的时候,这个FactoryBean一般都是我们引入的jar包中,人家给写好了的,我们会用就行,但知道原理也是好的!
例:通过FactoryBean接口的实现类CarFactoryBean,spring中配Car的bean:id="bwn"是CarFactoryBean的bean名,再通过构造方法注入Car的bean,则试:输出Car对象的toString()中的内容
CarFactoryBean.java
public class CarFactoryBean implements FactoryBean<Car>{
private String type;
public CarFactoryBean(String type) {
this.type=type;
}
//返回我们要配置的Bean 对象
@Override
public Car getObject() throws Exception {
return new Car(101,type);
}
//返回我们配置的Bean 对象的类型
@Override
public Class<?> getObjectType() {
return Car.class;
}
Car.java
public class Car {
private int id;
private String mark; //品牌
//get/set/toString,方法
}
factoryBean.xml
<!-- 通过FactoryBean实现类CarFactoryBean,来注入一个宝码的Car实例对象 -->
<bean id="bwm" class="cn.ybzy.springdemo.model.CarFactoryBean">
<constructor-arg value="BWM"></constructor-arg><!-- 构造方法注入,当然也可属性注入,但我没有写type的set方法 -->
</bean>
测试类中:
@Test
public void FactoryBeantest() {
ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
Car baomaCar = (Car) ct.getBean("bwm");
System.out.println(baomaCar);
}
----------------------------------------------------------------------------------------------------------------
Car [id=101, mark=BWM]
通过整合Quartz框架-定时做事用的,来演示看看,实际项目应用中,是怎么用FactoryBean来将第三方框架整合进入spring的!
1、导入spring-context-support,quartz等相关jar包
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.35</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-nop -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.35</version>
<scope>test</scope>
</dependency>
2、定义一个工作任务job
public class MyJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz我的具体的每次工作任务");
}
}
3、Spring配置文件中整合配置:
1)定义工作任务的Job
2)定义触发器Trigger,并将触发器与工作任务绑定
3)定义调度器,并将Trigger注册到Scheduler
!-- 定义工作任务的Job bean -->
<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 指定job的名称 -->
<property name="name" value="myQuartzJob"></property>
<!-- 指定job的分组 -->
<property name="group" value="myJobGroup"></property>
<!-- 指定具体的job类 -->
<property name="jobClass" value="cn.ybzy.springdemo.model.MyJob"></property>
<!-- 必须置为true,如果为false ,当没有活动的触发器与之关联时会在调度器中会删除该任务 -->
<property name="durability" value="true"></property>
<!--springIOC容器的KEY -->
<property name="applicationContextJobDataKey" value="factoryBean"></property>
</bean>
<!-- 定义触发器Trigger,并将触发器与工作任务绑定 -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="myTrigger"></property>
<property name="group" value="myGroup"></property>
<!-- 指定Trigger绑定的job -->
<property name="jobDetail" ref="myJob"></property>
<!-- 指定Cron的表达式,当前是每隔3秒运行一次 -->
<property name="cronExpression" value="0/3 * * * * ?"></property>
</bean>
<!-- 调度器,并把Trigger注册到Scheduler -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="trigger"/>
</list>
</property>
</bean>
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
CronTrigger配置完整格式为: [秒] [分] [小时] [日] [月] [周] [年]
*表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。
? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。
例如:要在每月的10号触发一个操作,但不关心是周几,
所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
- 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。
, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发
/ 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。
测试
//FactoryBean测试,FactoryBean quartz定时任框架测试,但在junit不支持多线程,所以看不到每三秒执行的效果,我
//另在model层中写一个类MyJob.java,用了main方法
@Test
public void FactoryBeantest() {
// ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
// Car baomaCar = (Car) ct.getBean("bwm");
// System.out.println(baomaCar);
ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
SchedulerFactoryBean scheduler=(SchedulerFactoryBean)ct.getBean("scheduler");
scheduler.start();
}
MyJobTest.java—model层中(无所谓那层了)
public class MyJobTest {
public static void main(String[] args) {
ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
SchedulerFactoryBean scheduler=(SchedulerFactoryBean)ct.getBean("scheduler");
scheduler.start();
}
}
----------------------------------------------------------------------------------------------------------------
quartz我的具体的每次工作任务
quartz我的具体的每次工作任务
quartz我的具体的每次工作任务
。。。。。。。。。
注解配bean
Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
Spring注解配置初始化对象(<bean>):
spring中使用注解配置对象前,要在配置文件中配置<context:component-scan >
标签告诉spring框架,配置了注解的类的位置配置文件applicationContext.xml:
<!--告诉sping,要创建的bean在springdemo包下及子包中所有类的bean-->
<context:component-scan base-package="cn.ybzy.springdemo">
</context:component-scan>
注解说明:
Component是最初spring框架设计的,后来为了标识不同代码层,衍生出Controller,Service,Repository三个注解 作用相当于配置文件的bean标签,被注解的类,spring始化时,就会创建该对象
@Component("user") 给类注解
@Service("user") // service层
@Controller("user") // web业务层
@Repository("user")//dao层
@Scope(scopeName="singleton") 等同于配置文件的scope属性
@Value(value="188") //给值属性赋值,可以用在方法上或者属性上
@Resource(name="car") //给对象赋值,该值car必须要已经声明(在配置文件中已经配置,或者在类对应中已经注解)
@PostConstruct //指定该方法在对象被创建后马上调用 相当于配置文件中的init-method属性
@PreDestroy //指定该方法在对象销毁之前调用 相当于配置文件中的destory-method属性
@Autowired 用的非常多
//自动装配对象赋值@Qualifier("car2") 一起使用 告诉spring容器自动装配哪个对象
例:Tiger.java,Monkey.java,Zoo.java,zhujieBean.xml,RunTest.java
@Component("tiger")
public class Tiger {
@Value(value="东南虎")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Tiger [name=" + name + "]";
}
}
@Component("monkey")
public class Monkey {
@Value("金丝猴")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Monkey [name=" + name + "]";
}
}
@Component
public class Zoo {
@Resource(name="tiger")
private Tiger tiger;
@Resource(type=Monkey.class)
private Monkey monkey;
public Tiger getTiger() {
return tiger;
}
public void setTiger(Tiger tiger) {
this.tiger = tiger;
}
public Monkey getMonkey() {
return monkey;
}
public void setMonkey(Monkey monkey) {
this.monkey = monkey;
}
@Override
public String toString() {
return "Zoo [tiger=" + tiger.getName() + ", monkey=" + monkey.getName() + "]";
}
}
<!-- 注解配bean -->
<!-- 1.先非注解配bean(对象),让我们作个比较 -->
<!--
<bean name="tiger" class="cn.ybzy.springdemo.model.Tiger" p:name="东南虎"></bean>
<bean name="monkey" class="cn.ybzy.springdemo.model.Monkey" p:name="金丝猴"></bean>
<bean name="nanchangzoo" class="cn.ybzy.springdemo.model.Zoo" p:monkey-ref="monkey" p:tiger-ref="tiger"></bean>
-->
<!-- 2.注解配bean(对象),让我们作个比较 -->
<context:component-scan base-package="cn.ybzy.springdemo.model"></context:component-scan>
测试
//注解配bean测试
@Test
public void zhujieBeantest() {
ApplicationContext ct = new ClassPathXmlApplicationContext("zhujieBean.xml");
Tiger tiger=(Tiger)ct.getBean("tiger");
System.out.println(tiger);
//Zoo zoo = (Zoo)ct.getBean("nanchangzoo");
Zoo zoo = (Zoo)ct.getBean("zoo");
System.out.println(zoo);
}
----------------------------------------------------------------------------------------------------------------
Tiger [name=东南虎]
Zoo [tiger=东南虎, monkey=金丝猴]
说一下@Resource的装配顺序:
1、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
2、指定了name或者type则根据指定的类型去匹配bean
3、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错
@Autowired
@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property,(如:Zoo,可以不用setTiger,setMonkey方法)。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。
因此,引入@Autowired注解,不要忘记配置文件要写:
<context:component-scan base-package="cn.ybzy.springdemo">
</context:component-scan>
然后才是在JavaBean的属性上加注解:
如果属性找不到对应的对象我不想让Spring容器抛 出异常,而就是显示null,可以吗?可以的,就是将@Autowired注解的required属性设置为false 即可:
@Autowired接口注入
上面的比较简单,我们只是简单注入一个Java类,那么如果有一个接口,有多个实现,Bean里引用的是接口名,又该怎么做呢?比如有一个Car接口:
Car接口有两个实现类,Spring并不知道应当引用哪个实现类。这种情况通常有两个解决办法:
- 删除其中一个实现类,Spring会自动去base-package下寻找Car接口的实现类,发现Car接口只有一个实现类,便会直接引用这个实现类
- 实现类就是有多个该怎么办?此时可以使用@Qualifier注解,指明你要spring装载那个对象:
最后提一下,还有一个功能和@Autowired差不多的一个注解@inject,它是jsr330规范的注解,用它的话要导入相应的jar包,我们推荐使用@Autowired
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>