我们想让骑士去营救少女
public class DemselRescuingKnight implements Knight{
/*
* 少女营救骑士类
*/
private RescueDamselQuest quest;//探秘
public DemselRescuingKnight() {
this.quest = new RescueDamselQuest();
}
//走上探险
public void embarkOnQuest() {
quest.embark();
}
}
这样写会造成耦合度太高,如果让骑士去杀龙或者其他探险就无能为力了,正确的做法是将骑士的任务作为参数传递给骑士,如下面的BraveKnight类
public interface Knight {
void embarkOnQuest();
}
public class BraveKnight implements Knight{
private Quest quest;
public BraveKnight(Quest quest) {
this.quest = quest;
}
public void embarkOnQuest() {
quest.embark();
}
}
接着实现具体的探险
public interface Quest {
void embark();
}
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类,并且初始化其任务
<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">
<constructor-arg ref="quest" />
bean>
<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
knight.embarkOnQuest();
context.close();
}
}
系统由许多不同的组件组成,每个组件除了自身核心的功能外,还承担着额外的职责,如日志,实物管理和安全。借助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为如下
<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">
<constructor-arg ref="quest" />
bean>
<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
knight.embarkOnQuest();
context.close();
}
}
Spring从两个角度来实现自动化装配
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean.
自动装配(autowiring):Spring自动满足bean之间的依赖
public interface MediaPlayer {
void play();
}
//这里用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 play();
}
@Component
public class SgtPeppers implements CompactDisc{
private String title = "title";
private String artist = "artist";
public void play() {
System.out.println( artist + " play " + title);
}
}
用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
player.play();
}
}
app-context.xml
<context:component-scan base-package="com.makenv.autoconfig" />
用app-context.xml初始化环境来测试
@ContextConfiguration(locations = "classpath:app-context.xml")
把上面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 CDPlayer(compactDisc);
}
}
构造注入Bean
构造注入字面量(包括装配集合)
@Autowired和@Component已经去掉
public class BlankDisc implements CompactDisc{
private String title;
private String artist;
//磁道列表
private List tracks;
public BlankDisc(String title,String artist,List 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: " + track);
}
}
}
app.xml代码如下
<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 Bandvalue>
<value>With a Little Help from My Friendsvalue>
<value>Lucy in the Sky with Diamondsvalue>
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
player.play();
}
}
属性注入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 Cake();
}
}
XML配置:
id="cake" class="com.makenv.part1.Cake" primary="true"/>
设置多个首选Bean也会错误,因为Spring无法选择
2.限定自动装配的Bean
2.1基于默认的Bean ID作为限定符,Bean的ID为首字母变为小写的类名,类名变更则会失效
@Qualifier("cake")
@Autowired
Dessert dessert;
2.2创建自定义的限定符
在Bean声明上添加@Qualifier注解
@Component
@Qualifier("soft")
public class Cake implements Dessert{
public void showType() {
System.out.println("Cake");
}
}
即可这样使用
@Qualifier("soft")
@Autowired
Dessert dessert;
Java显示配置时
@Bean
@Qualifier("soft")
public Dessert cake() {
return new Cake();
}
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 dessert;
这样依旧能能过两个不同的注解唯一确定一个Bean
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 Audience();
}
}
测试类
@RunWith(SpringJUnit4ClassRunner.class)
//用来初始化Spring的上下文环境
@ContextConfiguration(classes = ConcertConfig.class)
public class PerformTest {
@Autowired
Performance performance;
@Test
public void play() {
//Silencing cell phones
//表演开始
//CLAP CALP!!
performance.perform();
}
}
可以将上面的观众切面的方法写成环绕通知,执行效果一样
@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 index);
}
public class BlankDisc implements CompactDisc {
private String title;
private String artist;
//磁道列表
private List tracks;
public void setTitle(String title) {
this.title = title;
}
public void setArtist(String artist) {
this.artist = artist;
}
public void setTracks(List tracks) {
this.tracks = tracks;
}
public void playTrack(int index) {
String str = tracks.get(index);
System.out.println(str);
}
}
@Aspect
public class TrackCounter {
/*
* 轨道计数类
*/
private Map trackCounts = new HashMap();
@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 tracks = new ArrayList();
tracks.add("A");
tracks.add("B");
tracks.add("C");
cd.setTracks(tracks);
return cd;
}
@Bean
public TrackCounter trackCounter() {
return new TrackCounter();
}
}
测试类
@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));
}
}
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(..))"/>
<aop:before method="silenceCellPhones" pointcut="execution(** com.makenv.part3.Performance.perform(..)))"/>
<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!!
performance.perform();
}
}
可以将上面的形式声明为环绕通知
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 index);
}
public class BlankDisc implements CompactDisc {
private String title;
private String artist;
//磁道列表
private List tracks;
public void setTitle(String title) {
this.title = title;
}
public void setArtist(String artist) {
this.artist = artist;
}
public void setTracks(List tracks) {
this.tracks = tracks;
}
public void playTrack(int index) {
String str = tracks.get(index);
System.out.println(str);
}
}
public class TrackCounter {
private Map trackCounts = new HashMap();
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>Avalue>
<value>Bvalue>
<value>Cvalue>
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));
}
}
请求方法可以出现的参数类型(部分) | 请求方法中可以返回的类型 (部分) |
---|---|
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允许多种方式将客户端中的数据传送到控制器的处理方法中,包括
@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 MVC静态资源处理
[1]http://www.cnblogs.com/fangqi/archive/2012/10/28/2743108.html