0
点赞
收藏
分享

微信扫一扫

Spring实战学习笔记

海滨公园 2022-08-09 阅读 113


Spring之旅

依赖注入

我们想让骑士去营救少女

public class DemselRescuingKnight implements Knight{

/*
* 少女营救骑士类
*/
private RescueDamselQuest quest;//探秘

public DemselRescuingKnight() {
this.quest = new RescueDamselQuest();
}

//走上探险
public void embarkOnQuest() {
quest.embark();
}
}

这样写会造成耦合度太高,如果让骑士去杀龙或者其他探险就无能为力了,正确的做法是将骑士的任务作为参数传递给骑士,如下面的BraveKnight类

public interface Knight {
void

public class BraveKnight implements Knight{

private Quest quest;

public BraveKnight(Quest quest) {
this.quest = quest;
}

public void embarkOnQuest() {
quest.embark();
}
}

接着实现具体的探险

public interface Quest {
void

public class SlayDragonQuest implements Quest{

/*
* 杀龙探险
*/
private PrintStream stream;

public SlayDragonQuest(PrintStream stream) {
this.stream = stream;
}

public void embark() {
//开始杀龙
stream.println("Embarking on quest to slay the dragon");
}
}

接着用一个knight.xml让框架帮我们实现BraveKnight类,并且初始化其任务

<?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="knight" class="com.knights.BraveKnight">
<!--注入quest bean-->
<constructor-arg ref="quest"
</bean>

<!--创建SlayDragonQuest-->
<bean id="quest" class="com.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}"
</bean>

</beans>

开始探险

public class KnightMain {

public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");
Knight knight = context.getBean(Knight.class);
//Embarking on quest to slay the dragon

应用切面

系统由许多不同的组件组成,每个组件除了自身核心的功能外,还承担着额外的职责,如日志,实物管理和安全。借助AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活地应用到系统中,你的核心应用甚至不知道它们的存在。
我们想让诗人在骑士探险前和探险后都赞美一下骑士

public class BraveKnight implements Knight{

private Quest quest;
private Minstrel minstrel;

public BraveKnight(Quest quest, Minstrel minstrel) {
this.quest = quest;
this.minstrel = minstrel;
}

public void embarkOnQuest() {
minstrel.singBeforeQuest();
quest.embark();
minstrel.singAfterQuest();
}
}

但是每个骑士类都带一个诗人,让骑士去命令诗人吟诗,这样合适吗?应该让诗人自己做这些事

public class Minstrel {

/*
* 吟游诗人
*/
private PrintStream stream;

public Minstrel(PrintStream stream) {
this.stream = stream;
}

public void singBeforeQuest() {
System.out.println("singBeforeQuest");
}

public void singAfterQuest() {
System.out.println("singAfterQuest");
}
}

更改knight.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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="knight" class="com.knights.BraveKnight">
<!--注入quest bean,构造器注入-->
<constructor-arg ref="quest"
</bean>

<!--创建SlayDragonQuest-->
<bean id="quest" class="com.knights.SlayDragonQuest">
<constructor-arg value="#{T(System).out}"
</bean>

<bean id="minstrel" class="com.knights.Minstrel">
<constructor-arg value="#{T(System).out}"/>
</bean>

<aop:config>
<aop:aspect ref="minstrel">
<!--定义切点-->
<aop:pointcut id="embark" expression="execution(* com.knights.BraveKnight.embarkOnQuest(..))"/>
<!--声明前置通知-->
<aop:before pointcut-ref="embark" method="singBeforeQuest"/>
<!--声明后置通知-->
<aop:after pointcut-ref="embark" method="singAfterQuest"/>
</aop:aspect>
</aop:config>

</beans>

再次运行KnightMain结果如下

public class KnightMain {

public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");
Knight knight = context.getBean(Knight.class);
//singBeforeQuest
//Embarking on quest to slay the dragon
//singAfterQuest

装配Bean

自动装配Bean

Spring从两个角度来实现自动化装配
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean.
自动装配(autowiring):Spring自动满足bean之间的依赖

public interface MediaPlayer {
void

//这里用Component注解是为了后面测试时注入用的
@Component
public class CDPlayer implements MediaPlayer{

private CompactDisc cd;

@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}

public void play() {
cd.play();
}
}

@Configuration
@ComponentScan
//ComponentScan是启用组件扫描,默认扫描与配置类相同的包,可以用basePackages属性指定包名
public class CDPlayerConfig

public interface CompactDisc {
/*
* 光盘抽象类
*/
void

@Component
public class SgtPeppers implements CompactDisc{

private String title = "title";
private String artist = "artist";

public void play() {
System.out.println( artist + " play "

用CDPlayerConfig初始化环境来测试

@RunWith(SpringJUnit4ClassRunner.class)
//用来初始化Spring的上下文环境
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest

@Autowired
private MediaPlayer player;

@Autowired
private CompactDisc cd;

@Test
public void cdShouldNotBeNull() {
//自己测试了一下,如果为空会报错
Assert.assertNotNull(cd);
}

@Test
public void play() {
//artist play title

app-context.xml

<!--开启组件扫描-->
<context:component-scan base-package="com.makenv.autoconfig"

用app-context.xml初始化环境来测试

@ContextConfiguration(locations = "classpath:app-context.xml")

通过Java代码装配Bean

把上面CDPlayer类和SgtPeppers类上的@Component和@Autowired去掉,不让它们自动装配,CDPlayerConfig的代码修改如下,用CDPlayerConfig类来初始化环境,测试结果符合预期

@Configuration
public class CDPlayerConfig

//Bean注解会告诉Spring这个方法会返回一个对象,该对象要注册为Spring应用上下文中的bean.
//方法体中包含了最终产生bean实例的逻辑,可以用name属性为bean指定名字
@Bean
public CompactDisc compactDisc() {
return new SgtPeppers();
}

@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new

通过XML装配Bean

构造注入Bean
构造注入字面量(包括装配集合)
@Autowired和@Component已经去掉

public class BlankDisc implements CompactDisc{

private String title;
private String artist;
//磁道列表
private List<String> tracks;

public BlankDisc(String title,String artist,List<String> tracks) {
this.title = title;
this.artist = artist;
this.tracks = tracks;
}

public void play() {
System.out.println("Playing " + title + " by " + artist);
for (String track : tracks) {
System.out.println("-Track: "

app.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="compactDisc" class="com.makenv.xmlconfig.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band"
<constructor-arg value="The Beatles"
<constructor-arg>
<list>
<value>Sgt. Pepper's Lonely Hearts Club Band</value>
<value>With a Little Help from My Friends</value>
<value>Lucy in the Sky with Diamonds</value>
</list>
</constructor-arg>
</bean>

<bean id="cdPlayer" class="com.makenv.xmlconfig.CDPlayer">
<constructor-arg ref="compactDisc"/>
</bean>

</beans>

测试可用

@RunWith(SpringJUnit4ClassRunner.class)
//用来初始化Spring的上下文环境
@ContextConfiguration(locations = "classpath:app.xml")
public class CDPlayerTest

@Autowired
private MediaPlayer player;

@Test
public void play() {
//Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
//-Track: Sgt. Pepper's Lonely Hearts Club Band
//-Track: With a Little Help from My Friends
//-Track: Lucy in the Sky with Diamonds

属性注入Bean
属性注入字面量

高级装配

条件化的Bean

处理自动装配的歧义性

有一个Dessert接口,3个类实现了这个接口

public interface Dessert {
public void showType();
}

public class Cake implements Dessert{
public void showType() {
System.out.println("Cake");
}
}

public class Cookies implements Dessert{
public void showType() {
System.out.println("Cookies");
}
}

public class IceCream implements Dessert{
public void showType() {
System.out.println("IceCream");
}
}

当要自动装配Dessert时,不知道要转配哪个,有如下解决方案
1.标识首选的Bean
用注解:

@Component
@Primary
public class Cake implements Dessert{
public void showType() {
System.out.println("Cake");
}
}

Java配置:

@Configuration
@ComponentScan
public class DessertConfig

@Bean
@Primary
public Dessert cake() {
return new

XML配置:

<bean id="cake" class="com.makenv.part1.Cake" primary="true"/>

设置多个首选Bean也会错误,因为Spring无法选择
2.限定自动装配的Bean
2.1基于默认的Bean ID作为限定符,Bean的ID为首字母变为小写的类名,类名变更则会失效

@Qualifier("cake")
@Autowired
Dessert

2.2创建自定义的限定符
在Bean声明上添加@Qualifier注解

@Component
@Qualifier("soft")
public class Cake implements Dessert{
public void showType() {
System.out.println("Cake");
}
}

即可这样使用

@Qualifier("soft")
@Autowired
Dessert

Java显示配置时

@Bean
@Qualifier("soft")
public Dessert cake() {
return new

2.3使用自定义的限定符注解
可以通过多个注解来确定Bean,如通过delicious和soft确定为cake,通过delicious和cold确定为icecream,自定义这些注解,就是名称不一样

/*
* CONSTRUCTOR 用于描述构造器
* FIELD 用于描述域
* METHOD 用于描述方法
* TYPE 用于描述类,接口(包括注解类型)或enum声明
* RUNTIME 在运行时有效(即运行时保留)
*/
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold

@Component
@Delicious
@Soft
public class Cake implements Dessert

@Component
@Delicious
@Cold
public class IceCream implements Dessert

@Delicious
@Soft
@Autowired
Dessert

这样依旧能能过两个不同的注解唯一确定一个Bean

Bean的作用域

Spring实战学习笔记_xml

运行时值注入

面向切面的Spring

使用注解创建切面

Spring实战学习笔记_spring_02

public interface Performance {
public void perform();
}

@Component
public class PerformanceImpl implements Performance{
public void perform() {
System.out.println("表演开始");
}
}

@Aspect
public class Audience

/*
* 定义观众切面
*/
@Pointcut("execution(** com.makenv.Performance.perform(..))")
public void performance() {}

//上面定义了performance就可以简写,否则得写成这样
//@Before("execution(** com.makenv.Performance.perform(..))")
@Before("performance()")
public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}

@AfterReturning("performance()")
public void applause() {
System.out.println("CLAP CALP!!");
}

@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Demanding a refund");
}

}

配置切面,并且注册Bean

@Configuration
//启用AspectJ自动代理,不然即便使用了AspectJ注解,但它并不会被视为切面
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig

@Bean
public Audience audience() {
return new

测试类

@RunWith(SpringJUnit4ClassRunner.class)
//用来初始化Spring的上下文环境
@ContextConfiguration(classes = ConcertConfig.class)
public class PerformTest

@Autowired
Performance performance;

@Test
public void play() {
//Silencing cell phones
//表演开始
//CLAP CALP!!

可以将上面的观众切面的方法写成环绕通知,执行效果一样

@Aspect
public class Audience2 {

@Pointcut("execution(** com.makenv.Performance.perform(..))")
public void performance() {}

@Around("performance()")
public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones2");
jp.proceed();
System.out.println("CLAP CALP!!2");
} catch (Throwable e) {
System.out.println("Demanding a refund2");
}
}
}

处理通知中的参数

public interface CompactDisc {
void playTrack(int

public class BlankDisc implements CompactDisc

private String title;
private String artist;
//磁道列表
private List<String> tracks;

public void setTitle(String title) {
this.title = title;
}

public void setArtist(String artist) {
this.artist = artist;
}

public void setTracks(List<String> tracks) {
this.tracks = tracks;
}

public void playTrack(int

@Aspect
public class TrackCounter

/*
* 轨道计数类
*/
private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();

@Pointcut("execution(* com.makenv.part2.CompactDisc.playTrack(int)) && args(trackNumber)")
public void trackPlayed(int trackNumber) {}

@Before("trackPlayed(trackNumber)")
public void countTrack(int trackNumber) {
int currentCount = getPlayCount(trackNumber);
trackCounts.put(trackNumber, currentCount + 1);
}

public int getPlayCount(int trackNumber) {
return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
}
}

@Configuration
@EnableAspectJAutoProxy
public class TrackCounterConfig

@Bean
public CompactDisc blackDisc() {
BlankDisc cd = new BlankDisc();
cd.setTitle("title");
cd.setArtist("artist");
List<String> tracks = new ArrayList<String>();
tracks.add("A");
tracks.add("B");
tracks.add("C");
cd.setTracks(tracks);
return cd;
}

@Bean
public TrackCounter trackCounter() {
return new

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TrackCounterConfig.class)
public class TrackCounterTest

@Autowired
private CompactDisc cd;

@Autowired
private TrackCounter counter;

//测试CompactDisc不为空
@Test
public void test() {
Assert.assertNotNull(cd);
}

@Test
public void testTrackCounter() {

cd.playTrack(1);
cd.playTrack(2);
cd.playTrack(2);
//BCC12(一行显示一个)
System.out.println(counter.getPlayCount(1));
System.out.println(counter.getPlayCount(2));
//测试条为绿色,测试通过
Assert.assertEquals(1,counter.getPlayCount(1));
Assert.assertEquals(2,counter.getPlayCount(2));
}
}

在XML中声明切面

Spring实战学习笔记_自动装配_03

public interface Performance {
public void perform();
}

public class PerformanceImpl implements Performance{
public void perform() {
System.out.println("表演开始");
}
}

public class Audience {

public void silenceCellPhones() {
System.out.println("Silencing cell phones");
}

public void applause() {
System.out.println("CLAP CALP!!");
}

public void demandRefund() {
System.out.println("Demanding a refund");
}

}

config.xml的形式如下

<bean id="audience" class="com.makenv.part3.Audience"/>
<bean class="com.makenv.part3.PerformanceImpl"/>
<aop:config>
<aop:aspect ref="audience">
<aop:pointcut id="performance" expression="execution(** com.makenv.part3.Performance.perform(..))"/>
<!--如果不提前定义pointcut,每次都得这样写-->
<aop:before method="silenceCellPhones" pointcut="execution(** com.makenv.part3.Performance.perform(..)))"/>
<!--以下是简写形式-->
<!--<aop:before method="silenceCellPhones" pointcut-ref="performance"/>-->
<aop:after-returning method="applause" pointcut-ref="performance"/>
<aop:after-throwing method="demandRefund" pointcut-ref="performance"/>
</aop:aspect>
</aop:config>

测试程序

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:config.xml")
public class PerformTest

@Autowired
Performance performance;

@Test
public void play() {
//Silencing cell phones
//表演开始
//CLAP CALP!!

可以将上面的形式声明为环绕通知

public class Audience2 {

public void watchPerformance(ProceedingJoinPoint jp) {
try {
System.out.println("Silencing cell phones3");
jp.proceed();
System.out.println("CLAP CALP!!3");
} catch (Throwable e) {
System.out.println("Demanding a refund3");
}
}

}

config2.xml

<bean id="audience" class="com.makenv.part3.Audience2"/>
<bean class="com.makenv.part3.PerformanceImpl"/>
<aop:config>
<aop:aspect ref="audience">
<aop:pointcut id="performance" expression="execution(** com.makenv.part3.Performance.perform(..))"/>
<aop:around method="watchPerformance" pointcut-ref="performance"/>
</aop:aspect>
</aop:config>

将初始环境换为config2.xml,测试正确
为通知传递参数

public interface CompactDisc {
void playTrack(int

public class BlankDisc implements CompactDisc

private String title;
private String artist;
//磁道列表
private List<String> tracks;

public void setTitle(String title) {
this.title = title;
}

public void setArtist(String artist) {
this.artist = artist;
}

public void setTracks(List<String> tracks) {
this.tracks = tracks;
}

public void playTrack(int

public class TrackCounter {

private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();

public void countTrack(int trackNumber) {
int currentCount = getPlayCount(trackNumber);
trackCounts.put(trackNumber, currentCount + 1);
}

public int getPlayCount(int trackNumber) {
return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
}
}

app.xml

<bean id="trackCounter" class="com.makenv.part4.TrackCounter"/>
<bean id="cd" class="com.makenv.part4.BlankDisc">
<property name="title" value="title"/>
<property name="artist" value="artist"/>
<property name="tracks">
<list>
<value>A</value>
<value>B</value>
<value>C</value>
</list>
</property>
</bean>
<aop:config>
<aop:aspect ref="trackCounter">
<aop:pointcut id="trackPlayed" expression="execution( * com.makenv.part4.CompactDisc.playTrack(int)) and args(trackNumber)"/>
<aop:before method="countTrack" pointcut-ref="trackPlayed"/>
</aop:aspect>
</aop:config>

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:app.xml")
public class TrackCounterTest

@Autowired
private CompactDisc cd;

@Autowired
private TrackCounter counter;

@Test
public void testTrackCounter() {

cd.playTrack(1);
cd.playTrack(1);
cd.playTrack(2);
//BBC21(一行显示一个)
System.out.println(counter.getPlayCount(1));
System.out.println(counter.getPlayCount(2));
//测试条为绿色,测试通过
Assert.assertEquals(2,counter.getPlayCount(1));
Assert.assertEquals(1,counter.getPlayCount(2));
}
}

构建Spring Web应用程序

请求方法可以出现的参数类型(部分)

请求方法中可以返回的类型 (部分)

javax.servlet.ServletRequest

ModelAndView

javax.servlet.http.HttpServletRequest

Model

javax.servlet.ServletResponse

View

javax.servlet.http.HttpServletResopnse

代表逻辑视图名的String

javax.servlet.http.HttpSession

void

配置静态资源处理的原因:如果将DispatcherServlet请求映射配置为”/”,则Spring MVC将捕获Web容器所有的请求,包括静态资源的请求,Spring MVC会将它们当成一个普通请求处理,因此找不到对应处理器将导致错误。配置静态资源处理后,Spring框架能够捕获所有URL的请求,同时又将静态资源的请求转由Web容器处理,这样就可以将DispatcherServlet的请求映射配置为”/”

编写基本控制器

@Controller()
@RequestMapping("/index")
public class HomeController

@RequestMapping(value = "/index1", method = RequestMethod.GET)
public ModelAndView home1() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("home1");
//重定向到home2页面
//modelAndView.setViewName("redirect:/index/index2");
//转发到home2页面
//modelAndView.setViewName("forward:/index/index2");
return modelAndView;
}

@RequestMapping(value = "/index2", method = RequestMethod.GET)
public String home2() {
//重定向到home1页面
//return "redirect:/index/index1";
//转发到home1页面
//return "forward:/index/index1";
return "home2";
}

}

接受请求的输入

SpringMVC允许多种方式将客户端中的数据传送到控制器的处理方法中,包括

  1. 查询参数
  2. 表单参数
  3. 路径变量

@Controller
@RequestMapping("/spittles")
public class SpittleController

@Autowired
SpittleServiceImpl spittleService;

@RequestMapping(method = RequestMethod.GET)
public String selectSpittles(Model model) {
model.addAttribute(spittleService.selectSpittles());
return "spittles";
}

//访问如下形式的url,spittles/show?spittleId=2
//RequestParam没有提供value时默认与参数名相同
@RequestMapping(value = "/show", method = RequestMethod.GET)
public String showSpittle(@RequestParam int spittleId, Model model) {
Spittle spittle = new Spittle();
spittle = spittleService.selectOne(spittleId);
model.addAttribute(spittleService.selectOne(spittleId));
return "spittle";
}

//没有提供参数时,指定默认值
@RequestMapping(value = "/show2", method = RequestMethod.GET)
public String showSpittle2(@RequestParam(value = "spittleId", defaultValue = "1") int spId, Model model) {
model.addAttribute(spittleService.selectOne(spId));
return "spittle";
}

//使用占位符时,RequestMapping的value值需要和PathVariable的value值一样
//访问如下形式的url,/spittles/1,PathVariable没有提供value时默认与参数名相同
@RequestMapping(value = "/{spittleId}", method = RequestMethod.GET)
public String showSpittle3(@PathVariable("spittleId") int spittleId, Model model) {
model.addAttribute(spittleService.selectOne(spittleId));
return "spittle";
}

}

检验表单

Spring实战学习笔记_xml_04

渲染Web视图

spring表单绑定标签库

Spring实战学习笔记_自动装配_05



举报

相关推荐

spring学习笔记

Spring学习笔记

spring学习笔记(七)

Spring学习笔记(三)

0 条评论